寄生式(parasitic)繼承是與原型式繼承緊密相關(guān)的一種思路。寄生式繼承的思路與寄生構(gòu)造函數(shù)和工廠模式類似,即創(chuàng)建一個(gè)僅用于封裝繼承過(guò)程的函數(shù),該函數(shù)在內(nèi)部以某種方式來(lái)增強(qiáng)對(duì)象,最后再像真地是它做了所有工作一樣返回對(duì)象。
function object(o) {
function F(){}
F.prototype = o;
return new F();
}
function createAnother(original) {
// 通過(guò)調(diào)用函數(shù)創(chuàng)建一個(gè)新對(duì)象
var clone = object(original);
// 以某種方式來(lái)增強(qiáng)這個(gè)對(duì)象
clone.sayHi = function() {
alert("hi");
}
return clone;
}
上例中,createAnother() 函數(shù)接收了一個(gè)參數(shù),作為新對(duì)象繼承的對(duì)象。然后,把這個(gè)對(duì)象(original)傳遞給 object() 函數(shù),將返回的結(jié)果賦值給 clone。再為 clone 對(duì)象添加一個(gè)新方法 sayHi(),最后返回 clone 對(duì)象。
var person = {
name: "Bert",
friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = createAnother(person);
anotherPerson.sayHi(); // Hi
上例中, person返回了一個(gè)新對(duì)象 ---- anotherPerson。新對(duì)象不僅具有 person 的所有屬性和方法,而且還有自己的 sayHi() 方法。
在主要考慮對(duì)象而不是自定義類型和構(gòu)造函數(shù)的情況下,寄生式繼承也是一種有用的模式。例子中使用的 object() 函數(shù)不是必須的,可以使用任何能夠返回新對(duì)象的函數(shù)都適用于此模式。
使用寄生式繼承來(lái)為對(duì)象添加函數(shù),會(huì)由于不能做到函數(shù)復(fù)用而降低效率;這一點(diǎn)和構(gòu)造函數(shù)模式類似。
寄生組合式繼承
組合式繼承是 JavaScript 最常用的繼承模式;不過(guò)它也有自己的不足。組合式繼承最大的問(wèn)題就是無(wú)論什么情況下,都會(huì)調(diào)用兩次超類型構(gòu)造函數(shù);一次是在創(chuàng)建子類型原型的時(shí)候,另一次是在子類型構(gòu)造函數(shù)內(nèi)部。
子類型最終會(huì)包含超類型對(duì)象的全部實(shí)例屬性,但我們不得不在調(diào)用子類型構(gòu)造函數(shù)時(shí)重寫這些屬性。
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); // 第二次調(diào)用 SuperType()
this.age = age;
}
SuperType.prototype = new SuperType(); // 第一次調(diào)用 SuperType()
SubType.prototype.sayAge = function() {
alert(this.age);
}
上例中,有兩組 name 和 colors 屬性:一組在實(shí)例上,一組在 SubType 原型中。這就是調(diào)用兩次 SuperType 構(gòu)造函數(shù)的結(jié)果,通過(guò)寄生組合式繼承可以解決該問(wèn)題。
寄生組合式繼承:通過(guò)借用構(gòu)造函數(shù)來(lái)繼承屬性,通過(guò)原型鏈的混成形式來(lái)繼承方法。(不必為了指定子類型的原型而調(diào)用超類型的構(gòu)造函數(shù),我們所需要的無(wú)非就是超類型原型的一個(gè)副本)本質(zhì)上,就是使用寄生式繼承來(lái)繼承超類型的原型,然后再將結(jié)果指定給子類型的原型。
function inheritPrototype(subType, superType) {
var prototype = object(superType.prototype); // 創(chuàng)建對(duì)象
prototype.constructor = subType; // 增強(qiáng)對(duì)象
subType.prototype = prototype; // 指定對(duì)象
}
上例中的 inheritPrototype() 函數(shù)實(shí)現(xiàn)了寄生組合式繼承的最簡(jiǎn)單形式。
- 接收兩個(gè)參數(shù):子類型構(gòu)造函數(shù)和超類型構(gòu)造函數(shù)。
- 在函數(shù)內(nèi)部,第一步是創(chuàng)建超類型原型的一個(gè)副本。第二部是為創(chuàng)建的副本添加 constructor 屬性,從而彌補(bǔ)因重寫原型而失去的默認(rèn)的 constructor 屬性。
- 將新創(chuàng)建的對(duì)象(副本)賦值給子類型的原型。
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);
})
這個(gè)例子的高效體現(xiàn)在它只調(diào)用了一次 SuperType 構(gòu)造函數(shù),并且因此避免了在 SubType.prototype 上面創(chuàng)建不需要的、多余的屬性。與此同時(shí),原型鏈還能保持不變;能夠正常使用 instanceof 和 isPrototypeOf()。
備注:對(duì)寄生組合式繼承的看法是,通過(guò)代碼模擬原型鏈的結(jié)構(gòu)
每個(gè)原型鏈無(wú)非就是 proto 屬性指向的原型連城的鏈,既然我們能夠通過(guò) prototype 指定對(duì)象的原型鏈,那么同樣的指定對(duì)象的構(gòu)造器,就相當(dāng)于完美實(shí)現(xiàn)一個(gè)原型繼承了。
而之前所涉及到的: 1、借用構(gòu)造函數(shù)繼承,實(shí)際上就是通過(guò)指定對(duì)象的構(gòu)造函數(shù),初始化一些需要的屬性;2、組合式繼承就是使用 prototype 加上 構(gòu)造函數(shù);3、寄生式繼承通過(guò)函數(shù)內(nèi)部做處理繼承所需要的函數(shù),在內(nèi)部添加自定義屬性,達(dá)到繼承效果。
這些都只是在變相的改變對(duì)象自身的 prototype,所以只要明白 prototype 指向什么,代表什么,那么玩轉(zhuǎn) js 的繼承就不在話下了。