前言
想要更多理解 javascript 就离不开原型和原型链
,javascript 的原型链实际上跟其他语言继承多态
那套东西类似,只不过实现细节各有不同罢了,整体核心逻辑应该是很相似的(内部肯定有很多不同的操作,导致实际内容千差万别),ES6 的 class 也是根据原型和原型链出现的
每一个函数都有原型和原型链
,因此原型、原型链也是函数的知识点,这下可能也更容易理解函数为什么也是对象了吧😂,因此早期 class 对象 基本上都是 function 函数 编写的,这要是有相关的代码,相信也能一看就明白了
class 我相信大家都了解,毕竟应该都学习过 class(类和对象)、继承、多态
,可以参考一下他们,本篇就不多介绍他们了(如果有了解过相关继承链的,应该很容易理解)
下面就介绍一下原型和原型链吧,并且介绍之前先简单介绍一下函数的调用
原型(原型对象)
每一个 function 函数
类型的数据都有一个 prototype
类型的属性,这个属性指向一个对象,那就是这个对象的原型对象
,原型对象就是 function 函数
的公共对象
原型对象的 constructor
指向该 function 函数
之前看到过一个很好的图,如下所示
举个原型的代码例子
ps
:根据函数创建的对象有个隐藏属性__proto__
属性指向原型(不推荐使用,仅仅用来表示关系),上面的都是使用函数调用,这个是根据函数创建的对象不一样哈(理解为类和对象)
ps2
:类似于 {}
实际上也是一个语法糖,相当于 new Object()
函数
function Person() {
this.name = '哈哈'
this.age = 0
}
//声明一个原型
Person.prototype.country = '中国'
Person.prototype.fullName = function () {
return this.name + this.age
}
const person = new Person()
console.log(person);
console.log(person.__proto__); //对象有个隐藏属性__proto__属性指向原型
console.log(person.__proto__.constructor); //指向原对象function函数
console.log(person.fullName());
原型链
原型链可以理解为是基于原型的基础上形成的关系链条(继承),也就是可以理解为我们原型的 __proto__ 又指向哪里呢
原型的最终原型函数实际上就是 Object 对象的原型函数
,并且该最终原型函数的 __proto__ 为 null
(和一些语言逻辑不一样哈)
下面是以前看到的一个图的逻辑
基础原型链就这么简单,是么?
类本身的原型、一切皆对象,函数原型对应的原型也是对象的原型
前面提到的 function 函数类型 的 原型函数 我们理解了,其对象的 __proto__ 是我们的原型函数
,那么类本身(不是他们创建的对象)的 __proto__ 是什么呢
,看着他们都像是Function函数的原型函数,可以理解为其他所有类本身都是由 Function 创建出来的对象
如下所示,可以看出,其他类都是由 Function 类创建出来的对象
//Person类本身的构造方法就是Function,剩下的都是
console.log(Person.constructor === Function); // true
console.log(Object.constructor === Function); // true
console.log(Function.constructor === Function); // true
//并且他们(类本身)的原型函数都是 Function 的原型函数
console.log(Person.__proto__ === Function.prototype); // true
console.log(Object.__proto__ === Function.prototype); // true
console.log(Function.__proto__ === Function.prototype); // true
得出了以下结论:
- 在js中,所有函数都可以看做是 Function 的实例,也就是的类本身(不是他们创建的对象)是由 Function 创建而来的
- 而 Person 类本身 和 Object 类本身都是函数,所以它们的构造函数就是 Function
- Function 本身也是函数,所以 Function 也是自己的实例,function 也是对象
- 众所周知 javascript 一切皆对象,函数原型对应的原型也是对象的原型
这种情况下,似乎某种情况下形成了一个奇妙的闭环,之前看到了这张图(加入了类的 __proto__
和 function
),很符合
扩展(函数的调用)
了解了原型、原型链,我们简单了解一下函数调用
正常函数的调用相信大家都明白,直接在某个类中 this.函数名()
即可调用该函数,一般理解为对象调用了其里面某个函数
实际上对象的调用和我们直观的调用逻辑是有点不一样的,而是函数的执行使用到了某个对象
,就像我们常见的 call 方法 function.call(对象,参数)
,根据传入的对象,确定我们的函数内更新的参数,实际上后续操作的内容就是传入该对象的内存空间
ps
:最外层的函数对应的对象可以理解为是 window 对象
每个创建声明的 function 都会形成一个固定的结构,包括函数、属性(实际也是组合函数),这个东西实际上就是类本身,类拥有 prototype 原型函数,并且原型链的存在也就是继承的东西,里面存在父类、祖父类等
调用一个类的函数,实际上就是通过该对象,定位到其所在类,然后再该类中查找该函数,不存在则去原型函数查找,原型找不动去原型的父类原型查找,一直查询到根类,找不到则会报错,找到了则将该对象传递给找到的函数,让对应函数操作该对象所在的内存空间
最后
ps
: __proto__
这个一看就不是正规调用的,实际使用 Object.getPrototypeOf()
用来获取原型函数就行了