原型和原型链
首先先从一个构造函数开始:
function Person() {...} const p = new Person();
其中,Person就是一个构造函数, p就是一个实例对象.
每个函数都有一个属性 prototype,该属性指向一个对象,即调用该构造函数生成的实例对象的原型.
那么什么是原型呢?
可以这么理解,每个对象A(除了null)在创建的时候都会与之关联另一个对象B,这个对象B就是对象A的原型,对象A会从对象B中继承属性.
此处说是继承也不太恰当,因为继承是指复制属性到本体,而对象可以访问到对象的原型上的属性不是复制下来的,更像是委托访问
那么此时,我们就有了一条关系图:
那么怎么联系实例对象p和p的原型之间的关系呢?
每一个对象(除了null)都有一个属性 __proto__,该属性会指向该对象的原型.
虽然
__proto__属性已经被各大浏览器厂商实现,但是其还不能成为一个标准规范.obj.__proto__可以看作是Object.getPrototypeOf(obj)的语法糖.
这个时候就会有:
既然实例对象和构造函数都有方法指向实例的原型,那么实例的原型有没有什么属性可以指向其构造函数或者实例吗?
--> 有的,每个实例的原型对象有一个属性 constructor 可以指向关联的构造函数.但是没有属性可以指向对应的实例对象,这是因为实例对象可能会有多个.
这个时候就会有:
那么问题来了,原型也是一个对象,那么既然是对象,就可以用最原始的方法来创建它:
const p_proto = new Object()
也就是说原型对象是由构造函数 Object 实例化而来的.那么我们就可以更新原型对象的关系:
那么p_proto的原型的原型对象是什么呢?(p的原型对象的原型对象的__proto__)
--> 其实是null,原型链已经到头了
众所周知,其实函数也是对象,只不过是一种特殊的对象.
const fn = new Function('a','b','return a+b'); //等价于 function fn(a,b) { return a+b; }
因此,函数Person也是一个通过new Function出来的一个实例对象,它的构造函数是Function.因此有:
同理,原型对象也会指向原型对象的原型.
既然函数也是对象,那么构造函数Function 和 Object也是对象,他们也有 __proto__.
因此最终的走向图为:
