雖然在ES6中有了繼承,使用extends關(guān)鍵字就能實(shí)現(xiàn)。本篇講的不是這種,而是ES6之前的幾種實(shí)現(xiàn)繼承的方式。
(一)原型鏈
ECMAScript中將原型鏈作為實(shí)現(xiàn)繼承的主要方法。其基本思想是利用原型讓一個(gè)引用類型繼承另一個(gè)引用類型的屬性和方法。(不理解原型鏈的童鞋們可以翻閱一下我之前的博客,里面有詳細(xì)的說(shuō)明)
實(shí)現(xiàn)原型鏈的一種基本模式

function SuperType(){
? ? ? this.prototype=true;
}
SuperType.prototype.getSuperValue=function(){
? ? ? returnthis.property;
}function SubType(){
? ? ? this.subproperty=false;
}//通過(guò)創(chuàng)建SuperType的實(shí)例繼承了SuperTypeSubType.prototype=new SuperType();
SubType.prototype.getSubValue=function(){
? ? ? returnthis.subproperty;
}varinstance=new SubType();
alert(instance.getSuperValue());? //true

上面的例子中,instance指向SubType的原型,SubType的原型又指向SuperType的原型。getSuperValue()方法仍然還在SuperType.prototype中,但property則位于SubType.prototype中。這是因?yàn)閜rototype是一個(gè)實(shí)例屬性,而getSuperValue()則是一個(gè)原型方法。
注意:原型鏈雖然很強(qiáng)大,可以實(shí)現(xiàn)繼承,但存在兩個(gè)主要的問(wèn)題。(1)包含引用類型值的原型屬性會(huì)被所有實(shí)例共享,這會(huì)導(dǎo)致對(duì)一個(gè)實(shí)例的修改會(huì)影響另一個(gè)實(shí)例。
(2)在創(chuàng)建子類型的實(shí)例時(shí),不能向超類型的構(gòu)造函數(shù)中傳遞參數(shù)。由于這兩個(gè)問(wèn)題的存在,實(shí)踐中很少單獨(dú)使用原型鏈。
以下例子清楚的說(shuō)明了第一個(gè)問(wèn)題

function SuperType(){
? ? ? this.colors=["red", "blue", "green"];
}function SubType(){
}//繼承了SuperTypeSubType.prototype=new SuperType();varinstance1=new SubType();
instance1.colors.push("black");
alert(instance1.colors);? //red,blue,green,blackvarinstance2=new SubType();
alert(instance2.colors);? ? //red,blue,green,black

(二)借用構(gòu)造函數(shù)
在解決原型中包含引用類型值所帶來(lái)的問(wèn)題中,使用借用構(gòu)造函數(shù)技術(shù)來(lái)解決。借用構(gòu)造函數(shù)的基本思想,即在子類型構(gòu)造函數(shù)的內(nèi)部調(diào)用超類型構(gòu)造函數(shù)。函數(shù)只不過(guò)是在特定環(huán)境中執(zhí)行代碼的對(duì)象,因此通過(guò)使用apply()和call()方法可以在新創(chuàng)建的對(duì)象上執(zhí)行構(gòu)造函數(shù)。如下例子

function SuperType(){
? ? ? this.colors=["red", "blue", "green"];
}function SubType(){
? ? ? //繼承SuperTypeSuperType.call(this);
}varinstance1=new SubType();
instance1.colors.push("black");
alert(instance1.colors);? //red,bllue,green,blackvarinstance2=new SubType();
alert(instance2.colors);? //red,blue,green

上面例子中,通過(guò)使用call()方法(或者apply()方法),在新創(chuàng)建的SubType實(shí)例的環(huán)境下調(diào)用了SuperType構(gòu)造函數(shù)。這樣一來(lái)就會(huì)在新的SubType對(duì)象上執(zhí)行SuperType()函數(shù)中定義的所有對(duì)象初始化代碼。結(jié)果,SubType的每個(gè)實(shí)例都會(huì)有自己的colors屬性副本。
相對(duì)于原型鏈而言,借用構(gòu)造函數(shù)可以在子類型構(gòu)造函數(shù)中向超類型構(gòu)造函數(shù)傳遞參數(shù)。如下例子

function SuperType(name){
? ? ? this.name=name;
}function SubType(){
? ? ? //繼承了SuperType,同時(shí)還傳遞了參數(shù)SuperType.call(this,"mary");
? ? ? //實(shí)例屬性this.age=22;
}varinstance=new SubType();
alert(instance.name);? //maryalert(instance.age);//29

借用構(gòu)造函數(shù)存在兩個(gè)問(wèn)題:(1)無(wú)法避免構(gòu)造函數(shù)模式存在的問(wèn)題,方法都在構(gòu)造函數(shù)中定義,因此無(wú)法服用函數(shù)。(2)在超類型的原型中定義的方法,對(duì)子類型而言是不可見(jiàn)的。因此這種技術(shù)很少單獨(dú)使用。
(三)組合繼承
組合繼承,指的是將原型鏈和借用構(gòu)造函數(shù)的技術(shù)組合到一起。思路是使用原型鏈實(shí)現(xiàn)對(duì)原型方法的繼承,而通過(guò)借用構(gòu)造函數(shù)來(lái)實(shí)現(xiàn)對(duì)實(shí)例屬性的繼承。這樣,既通過(guò)在原型上定義方法實(shí)現(xiàn)了函數(shù)的復(fù)用,又能夠保證每個(gè)實(shí)例都有它自己的屬性。以下例子充分說(shuō)明了這一點(diǎn)

function SuperType(name){
? ? ? this.name=name;
? ? ? this.colors=["red", "blue", "green"];
}
SuperType.prototype.sayName=function(){
? ? ? alert(this.name);
};function SubType(name, age){
? ? ? //繼承屬性? ? 使用借用構(gòu)造函數(shù)實(shí)現(xiàn)對(duì)實(shí)例屬性的繼承SuperType.call(this,name);
? ? ? this.age=age;
}//繼承方法? ? 使用原型鏈實(shí)現(xiàn)SubType.prototype=new SuperType();
SubType.prototype.constructor=SubType;
subType.prototype.sayAge=function(){
? ? ? alert(this.age);
};varinstance1=newSubType("mary", 22);
instance1.colors.push("black");
alert(instance1.colors);? //red,blue,green,blackinstance1.sayName();//maryinstance1.sayAge();//22varinstance2=newSubType("greg", 25);
alert(instance2.colors);? //red,blue,greeninstance2.sayName();//greginstance2.sayAge();//25

這個(gè)例子中,兩個(gè)實(shí)例既分別擁有自己的屬性,包括colors屬性,又可以使用相同的方法。
組合繼承避免了原型鏈和借用構(gòu)造函數(shù)的缺點(diǎn),融合了他們的優(yōu)點(diǎn),是JavaScript中最常用的繼承模式。