继承是OO语言中的一个最为人津津乐道的概念。下面分别来细数一下JavaScript中最常用的五种继承方式:
一、原型链
实现原型链有一种基本模式,其代码大致如下:
function SuperType(){
// 超类型的构造函数
}
function SubType(){
// 子类型的构造函数
}
SubType.prototype = new SuperType(); // 原型链继承方式
var instance = new SubType();
原型链继承存在的问题:
- 超类型的实例属性变成子类型的原型属性导致超类型中的引用类型的实例属性被所有子类型共享,一个子类型更改其属性会影响到其它子类型。
- 在创建子类型的实例时,不能向超类型的构造函数中传递参数。
因此实践中很少会单独使用原型链。
二、借用构造函数
这种技术的基本思想相当简单,即在子类型构造函数的内部调用超类型构造函数。如下所示:
function SuperType(){
// 超类型的构造函数
}
function SubType(){
SuperType.call(this); // 借用构造函数继承方式
}
var instance = new SubType();
借用构造函数继承存在的问题:
- 方法都在构造函数中定义,函数复用无从谈起。
- 在超类型的原型中定义的方法,对子类型而言是不可见的。
考虑到这些问题,借用构造函数的技术也是很少单独使用的。
三、组合继承
组合继承指的是将原型链和借用构造函数的技术组合到一块,从而发挥两者之长的一种继承模式。其背后的思路是使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。这样,既通过在原型上定义方法实现了函数复用,又能保证每个实例都有它自己的属性。组合继承的实现方法如下:
function SuperType(){
// 超类型的构造函数
}
function SubType(){
SuperType.call(this); // 借用超类型的构造函数
}
SubType.prototype = new SuperType(); // 原型链继承
SubType.prototype.constructor = SubType; // 恢复指针指向
var instance = new SubType();
组合继承避免了原型链和借用构造函数的缺陷,融合了它们的优点,成为JavaScript中最常用的继承模式。
不过,它也有自己的不足。组合继承最大的问题就是无论什么情况下,都会调用两次超类型构造函数:一次是在创建子类型原型的时候,另一次是在子类型构造函数内部。这样做导致的结果就是超类型的实例属性既存在于子类型的实例中,又存在于子类型的原型中。
四、寄生式继承
寄生式继承的思路是:创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再返回增强之后的对象。以下代码示范了寄生式继承模式:
function createAnother(original){
var clone = object(original); // 通过调用函数创建一个新对象
// 以某种方式来增强这个对象
return clone; // 返回这个对象
}
var person = {};
var anotherPerson = createAnother(person);
在主要考虑对象而不是自定义类型和构造函数的情况下,寄生式继承也是一种有用的模式。
但是使用寄生式继承来为对象添加函数,会由于不能做到函数复用而降低效率;这一点与构造函数模式类似。
五、寄生组合式继承
寄生组合式继承背后的基本思路是:不必为了指定子类型的原型而调用超类型的构造函数,我们所需的无非就是超类型原型的一个副本而已。本质上,就是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型。寄生组合式继承的基本模式如下所示:
function inheritPrototype(subType, superType){
var prototype = object(superType.prototype); // 创建对象
prototype.constructor = subType; // 增强对象
subType.prototype = prototype; // 指定对象
}
function SuperType(){
// 超类型的构造函数
}
function SubType(){
SuperType.call(this); // 借用构造函数
}
inheritPrototype(SubType, SuperType);
var instance = new SubType();
寄生组合式继承的高效率体现在它只调用了一次SuperType构造函数,并且因此避免了在SubType.prototype上面创建不必要的、多余的属性。开发人员普遍认为寄生组合式继承是引用类型最理想的继承范式。