基于__proto__和prototype的原型链
关于__proto__属性,MDN上的解释如下:
The proto property of Object.prototype is an accessor property (a getter function and a setter function) that exposes the internal [[Prototype]] (either an object or null) of the object through which it is accessed.
即是说,__proto__ 属性指向了实例对象的原型Constructor.prototype。
首先用一张图来总结__proto__和prototype的关系:

实例的__proto__
function Foo() {}
var f1 = new Foo();
f1.__proto__ == Foo.prototype; //true
通过new方法创建函数Foo的实例f1,f1.__proto__会指向Foo.prototype,进而继承Foo函数原型上的所有属性和方法。
在JS中,只有函数有prototype属性,基于prototype可以去模拟实现类和继承。
函数原型和构造器
f1.constructor === Foo.prototype.constructor; //true
Foo.prototype.constructor === Foo; //true
f1.constructor === Foo; //true
f1是Foo的示例,它的constructor就是Foo函数原型对象中的constructor,而Foo函数原型上的constructor就指向函数本身。
对象的__proto__
var one = {x: 1};
var two = new Object();
one.__proto__ === Object.prototype // true
two.__proto__ === Object.prototype // true
one.toString === one.__proto__.toString // true
不管是隐式还是显式创建的对象,对象的__proto__都是Object.prototype,Object实际上是一个js函数function Object(){},所以拥有prototype属性。two = new Object()中Object是构造函数,所以two.__proto__就是Object.prototype。至于one,ES规范定义对象字面量的原型就是Object.prototype。
one.constructor === Object.prototype.constructor;
Object.prototype.constructor === Object;
one.constructor === Object;
函数的__proto__
Foo.constructor === Function; //true
Foo.__proto__ === Function.prototype; // true
函数Foo的构造函数是function Function(){},所以函数Foo的__proto__都指向Function.prototype。
Function.__proto__ === Function.prototype; // true
Array.__proto__ === Function.prototype; // true
Object.__proto__ === Function.prototype; // true
String.__proto__ === Function.prototype; // true
而Function本身就是函数,所以Function.__proto__就是Function.prototype,二者为同一对象。同理,Object/Array/String等等构造函数本质上和Function一样,均继承于Function.prototype。
Function.prototype的__proto__
Function.prototype直接继承自Object.prototype,Function.prototype.__proto__就是Object.prototype,二者为同一对象。
Function.prototype.__proto__ === Object.prototype; //true
Function.prototype instanceof Object; //true
Function.prototype instanceof Function; //false
通过这点我们可以弄清继承的原型链:Object.prototype--->Function.prototype--->Function|Object|Array...。。 综上所述可以得出:
Function.__proto__.__proto__ === Object.prototype;
所以Function是Object的实例
Function instanceof Object; //true
此外Object作为函数,继承了Function.prototype的方法,所以Object又是Function的实例。
Object.__proto__ === Function.prototype;
Object instanceof Function; //true
哈哈,二者互为实例,这就是有名的鸡生蛋和蛋生鸡的关系。
Object.prototype的__proto__
原型链的尽头(root)是Object.prototype。所有对象均从Object.prototype继承属性。
Object.prototype.__proto__ === null; //true
REFS:
es6继承
class通过extends实现继承,比起es5基于原型链、借用构造函数实现继承方便很多。
class A {
constructor() {
this.x = 1;
}
}
class B extends A {
constructor() {
super();
this.x = 2;
super.x = 3;
console.log(super.x); // undefined
console.log(this.x); // 3
}
}
let b = new B();
上面代码中,super.x赋值为3,这时等同于对this.x赋值为3。而当读取super.x的时候,读的是A.prototype.x,所以返回undefined。 这是由于this指向子类实例,所以如果通过super对某个属性赋值,这时super就是this,赋值的属性会变成子类实例的属性。
- set的时候,super为this子类实例
- get的时候,super获取的是A.prototype
大多数浏览器的 ES5 实现之中,每一个对象都有__proto__属性,指向对应的构造函数的prototype属性。
function test(){}
test.__proto__ = test.constructor.prototype
es6中extends实现的继承,同时有prototype属性和__proto__属性,因此同时存在两条继承链。
B.__proto__ === A
B.prototype.__proto__ === A.prototype
//对应es5实现
B.prototype = new A()
实现继承的关键:
- 子类B.prototype的
__proto__属性指向A.prototype,即B.prototype.__proto__ = A.prototype,表示将继承父类原型上的属性和方法; - 子类B的
__proto__属性总是指向父类A,即B.__proto__ = A,表示将继承父类的静态属性和方法。
这样的结果是因为,类的继承是按照下面的模式实现的。
class A {}
class B {}
// B 的实例继承 A 的实例
Object.setPrototypeOf(B.prototype, A.prototype);
// B 继承 A 的静态属性
Object.setPrototypeOf(B, A);
const b = new B();
Object.setPrototypeOf方法的实现,会将proto对象作为obj的原型。
Object.setPrototypeOf = function (obj, proto) {
obj.__proto__ = proto;
return obj;
}
本质上,class关键字只是原型的语法糖,JavaScript继承仍然是基于原型实现的。