实现继承是 ECMAScript 唯一支持的继承方式,而这主要是通过原型链实现的
原型链继承
构造函数、原型和实例的关系
- 每个构造函数都有一个原型对象: Function.prototype
- 原型有一个属性(constructor)指回构造函数: Function.prototype.constructor = Function
- 实例有一个内部指针([[ prototype ]])指向原型: function_instance.__proto__ = Function.prototype
1 | /* 构造函数 */ |
插入图片讲解
实现继承
1 | function Father(){ |
插入图片
原型与实例的关系
- 方式一
1 | /* 上面用到了instanceof来检测原型与实例的关系 */ |
- 方式二
确定这种关系的第二种方式是使用 isPrototypeOf()方法。原型链中的每个原型都可以调用这个方法,只要原型链中包含这个原型,这个方法就返回 true:
1 | Father.prototype.isPrototypeOf(s);//true ,s的原型链中包含Father原型 |
原型链的问题
- 某一实例成为构造函数原型后,共享内存的问题
1 | function Father(){ |
- 原型链的第二个问题是,子类型在实例化时不能给父类型的构造函数传参。就导致原型链基本不会被单独使用,用来修改继承关系。
盗用构造函数继承
1 | function Father(name){ |
盗用构造函数的问题:子类也不能访问父类原型上定义的方法,所以基本也不用此方法来修改继承关系
组合继承
组合继承(有时候也叫伪经典继承)综合了原型链和盗用构造函数,将两者的优点集中了起来。基本的思路是使用原型链继承原型上的属性和方法,而通过盗用构造函数继承实例属性。
1 | function Father(name){ |
组合继承弥补了原型链和盗用构造函数的不足,是 JavaScript 中使用最多的继承模式。而且组合继承也保留了 instanceof 操作符和 isPrototypeOf()方法识别合成对象的能力。
寄生式组合继承
其实组合继承也有一个问题:效率不是很高,因为它要执行两次父级构造函数,第一次在子级构造函数内,第二次在修改子级原型时
1 | /* 寄生式顾名思义,在一个函数内部做了某些操作 */ |
这里只调用了一次 Father 构造函数,避免了 Son.prototype 上不必要也用不到的属性,
因此可以说这个例子的效率更高。而且,原型链仍然保持不变,因此 instanceof 操作符和
isPrototypeOf()方法正常有效。寄生式组合继承可以算是引用类型继承的最佳模式。