constructor、__proto__和prototype
constructor、__proto__、prototype组成的复杂网络
先给出一段代码:
function Person() {}
var person = new Person();
var object = new Object();
根据这段代码,可以画出下图:
图看起来很复杂,但其实可以得出两个非常简洁的结论:
- 任何一个对象都有一个constructor指向它的构造函数,有一个__proto__指向它的原型;
- person是对象,它的constructor指向Person,它的__proto__指向Person.prototype;
- object是对象,它的constructor指向Object,它的__proto__指向Object.prototype;
- 原型也是对象,它的constructor指向Object,它的__proto__指向Object.prototype(除了Object.prototype比较特殊,它的__proto__指向null);
- 构造函数也是对象,它的constructor指向Function,它的__proto__指向Function.prototype;
- 任何一个构造函数有一个prototype指向其所创建的对象的原型(注意,不是该构造函数的原型,因为构造函数的原型是Function.prototype)。
理解原型链
接下来,根据上面得出的结论我们来理解一下原型链。我们知道,从一个对象上读取属性,如果在该对象上找不到,则会通过__proto__去它的原型上查找。
现在我们要读取person.name,由于person上没有name属性,于是会去person的原型Person.prototype上查找;由于Person.prototype上也没有,因此会去Person.prototype的原型上查找。别忘了,原型也是对象,如果在原型上找不到指定的属性,同样会去它的原型上查找。Person.prototype是Object的实例,因此Person.prototype的原型是Object.prototype。Object.prototype上也没有name属性,会继续去Object.prototype的原型上查找。而Object.prototype是Object的实例,那么它的原型也应该是Object.prototype,但这样显然陷入了死循环,因此把Object.prototype的原型设为null。