前文說過,組合繼承是javascript最常用的繼承模式,不過,它也有自己的不足:組合繼承無論在什么情況下,都會調用兩次父類構造函數,一次是在創建子類原型的時候,另一次是在子類構造函數內部.子類最終會包含父類對象的全部實例屬性,但我們不得不在調用子類構造函數時重寫這些屬性.請再看一次組合繼承的例子:
function SuperType(name){ this.name=name; this.friends=["gay1","gay2"]; } SuperType.prototype.sayName=function(){ alert(this.name); }; funciton SubType(name,age){ SuperType.call(this,name); //第二次調用SuperType(); this.age=age; } SubType.prototype=new SuperType(); //第一次調用SuperType() SubType.prototype.sayAge=function(){ alert(this.age); };
在第一次調用SuperType構造函數時,SubType.prototype會得到兩個屬性:name和friends,他們都是SuperType的實例屬性.只不過現在位於SubType的原型中.當調用SubType構造函數時,又會調用一次SuperType構造函數,這一次又在新對象上創建了實例屬性name和friends.於是,這兩個屬性就屏蔽了原型中的兩個同名屬性.
結果是,有兩組name和friends屬性,一組在SubType的實例上,一組在SubType的原型上.這就是調用兩次SuperType構造函數的結果.而現在,找到了解決這個問題的方法:寄生組合式繼承.
寄生組合式繼承:通過借用構造函數來繼承屬性,通過原型鏈的混成形式來繼承方法.思路:不必為了指定子類的原型而調用父類的構造函數,我們所需要的無非就是父類原型的一個副本而已.本質上,就是使用寄生式繼承來繼承父類的原型,然后在將結果指定給子類的原型:
function inheritPrototype(subType,superType){ var prototype=object(superType.prototype); //創建父類原型的一個副本 等同於使用Object.create(superType.prototype) prototype.constructor=subType; //為副本添加constructor屬性,彌補重寫原型而失去的constructor屬性 subType.prototype=prototype; //將創建的對象(副本)賦值給子類的原型 }
這樣,我們就可以通過調用inheritPrototype()函數,替換前面例子中為子類原型的賦值語句了:
function inheritPrototype(subType,superType){ var prototype=Object.create(superType.prototype); //創建父類原型的一個副本 等同於使用Object.create(superType.prototype) prototype.constructor=subType; //為副本添加constructor屬性,彌補重寫原型而失去的constructor屬性 subType.prototype=prototype; //將創建的對象(副本)賦值給子類的原型 } function SuperType(name){ this.name=name; this.friends=["gay1","gay2"]; } SuperType.prototype.sayName=function(){ alert(this.name); }; function SubType(name,age){ SuperType.call(this,name); //繼承SuperType this.age=age; //擴展出age屬性 } inheritPrototype(SubType,SuperType); SubType.prototype.sayAge=function(){ alert(this.age); };//擴展出sayAge方法 var person1=new SubType("nUll",25); var person2=new SubType("mywei",25); person1.friends.push("gay3"); person1.sayName(); person1.sayAge(); alert(person1.friends); //gay1,gay2,gay3 alert(person2.friends); //gay1,gay2 alert(person1 instanceof SubType); //true alert(person1 instanceof SuperType); //true alert(SubType.prototype.isPrototypeOf(person1)); //true alert(SuperType.prototype.isPrototypeOf(person1)); //true
這個例子的高效率體現在它只調用了一次SuperType構造函數,並且因此避免了在SubType.prototype上創建不必要的 多余的屬性.與此同時,原型鏈還能保持不變.因此,還能夠正常使用instanceof 和isPrototypeOf確定繼承關系.
YUI的YAHOO.lang.extend()方法采用了寄生組合式繼承。