寄生式(parasitic)继承是与原型式继承紧密相关的一种思路。寄生式继承的思路与寄生构造函数和工厂模式类似,即创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像真地是它做了所有工作一样返回对象。
function object(o) {
function F(){}
F.prototype = o;
return new F();
}
function createAnother(original) {
// 通过调用函数创建一个新对象
var clone = object(original);
// 以某种方式来增强这个对象
clone.sayHi = function() {
alert("hi");
}
return clone;
}
上例中,createAnother() 函数接收了一个参数,作为新对象继承的对象。然后,把这个对象(original)传递给 object() 函数,将返回的结果赋值给 clone。再为 clone 对象添加一个新方法 sayHi(),最后返回 clone 对象。
var person = {
name: "Bert",
friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = createAnother(person);
anotherPerson.sayHi(); // Hi
上例中, person返回了一个新对象 ---- anotherPerson。新对象不仅具有 person 的所有属性和方法,而且还有自己的 sayHi() 方法。
在主要考虑对象而不是自定义类型和构造函数的情况下,寄生式继承也是一种有用的模式。例子中使用的 object() 函数不是必须的,可以使用任何能够返回新对象的函数都适用于此模式。
使用寄生式继承来为对象添加函数,会由于不能做到函数复用而降低效率;这一点和构造函数模式类似。
寄生组合式继承
组合式继承是 JavaScript 最常用的继承模式;不过它也有自己的不足。组合式继承最大的问题就是无论什么情况下,都会调用两次超类型构造函数;一次是在创建子类型原型的时候,另一次是在子类型构造函数内部。
子类型最终会包含超类型对象的全部实例属性,但我们不得不在调用子类型构造函数时重写这些属性。
function SuperType(name) {
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function() {
alert(this.name);
}
function SubType(name, age) {
SuperType.call(this, name); // 第二次调用 SuperType()
this.age = age;
}
SuperType.prototype = new SuperType(); // 第一次调用 SuperType()
SubType.prototype.sayAge = function() {
alert(this.age);
}
上例中,有两组 name 和 colors 属性:一组在实例上,一组在 SubType 原型中。这就是调用两次 SuperType 构造函数的结果,通过寄生组合式继承可以解决该问题。
寄生组合式继承:通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。(不必为了指定子类型的原型而调用超类型的构造函数,我们所需要的无非就是超类型原型的一个副本)本质上,就是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型。
function inheritPrototype(subType, superType) {
var prototype = object(superType.prototype); // 创建对象
prototype.constructor = subType; // 增强对象
subType.prototype = prototype; // 指定对象
}
上例中的 inheritPrototype() 函数实现了寄生组合式继承的最简单形式。
- 接收两个参数:子类型构造函数和超类型构造函数。
- 在函数内部,第一步是创建超类型原型的一个副本。第二部是为创建的副本添加 constructor 属性,从而弥补因重写原型而失去的默认的 constructor 属性。
- 将新创建的对象(副本)赋值给子类型的原型。
function SuperType(name) {
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function() {
alert(this.name);
}
function SubType(name, age) {
SuperType.call(this, name);
this.age = age;
}
inheritPrototype(SubType, SuperType);
SubType.prototype.sayAge = function( {
alert(this.age);
})
这个例子的高效体现在它只调用了一次 SuperType 构造函数,并且因此避免了在 SubType.prototype 上面创建不需要的、多余的属性。与此同时,原型链还能保持不变;能够正常使用 instanceof 和 isPrototypeOf()。
备注:对寄生组合式继承的看法是,通过代码模拟原型链的结构
每个原型链无非就是 proto 属性指向的原型连城的链,既然我们能够通过 prototype 指定对象的原型链,那么同样的指定对象的构造器,就相当于完美实现一个原型继承了。
而之前所涉及到的: 1、借用构造函数继承,实际上就是通过指定对象的构造函数,初始化一些需要的属性;2、组合式继承就是使用 prototype 加上 构造函数;3、寄生式继承通过函数内部做处理继承所需要的函数,在内部添加自定义属性,达到继承效果。
这些都只是在变相的改变对象自身的 prototype,所以只要明白 prototype 指向什么,代表什么,那么玩转 js 的继承就不在话下了。