一、原型鏈
實現方法:讓一個原型對象等於另一個類型的實例
function SuperType(){
this.prototype=true;
}
SuperType.prototype.getSuperValue=function(){
return this.property;
};
function SubType(){
this.subproperty=false;
}
//繼承了SuperType
SubType.prototype=new SuperType();
SubType.prototype.getSubValue=function(){
return this.subproperty;
};
var instance=new SubType();
alert(instance.getSuperValue()); //true
上面的例子中,調用instance.getSuperValue()會經歷三個搜索步驟:
1.搜索實例
2.搜索SubType.prototype
3.搜索SuperType.prototype
注意:
在通過原型鏈實現繼承時,不能使用對象字面量創建原型方法,因爲這樣做會重寫原型鏈。
原型鏈的問題:
1.包含引用類型值的原型屬性會被所有實例共享
2.在創建子類型的實例時,不能向超類型的構造函數中傳遞參數
二、借用構造函數
這種技術的基本思想是:在子類型構造函數的內部調用超類型構造函數
function SuperType(){
this.colors=["red","blue","green"];
}
function SubType(){
//繼承了SuperType
SuperType.call(this);
}
var instance1=new SubType();
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
var instance2=new Subtype();
alert(instance2.colors); //"red,blue,green"
以上代碼在(未來將要)新創建的SubType實例的環境下調用了SuperType構造函數。這樣一來,就會在新SubType對象上執行SuperType()函數中定義的所有對象初始化代碼。結果,SubType的每個實例就會具有自己的colors屬性的副本了。
借用構造函數還可以傳遞參數:
function SuperType(name){
this.name=name;
}
function SubType(){
//繼承了SuperType,同時還傳遞了參數
SuperType.call(this,"Nicholas");
//實例屬性
this.age=29;
}
var instance =new SubType();
alert(instance.name); //"Nicholas"
alert(this.age); //29
借用構造函數中的問題:
1.如果僅僅借用構造函數,那麼將無法避免構造函數模式存在的問題——方法都在構造函數中定義,函數複用無從談起
2.在超類型的原型中定義的方法,對子類型而言是不可見的,結果所有類型都只能使用構造函數模式。所以這種方法很少單獨使用。
三、組合繼承
思路是使用原型鏈實現對原型屬性和方法的繼承,而通過借用構造函數來實現對實例屬性的繼承
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;
}
//繼承方法
SubType.prototype=new SuperType();
SubType.prototype.constructor=SubType;
SubType.prototype.sayAge=function(){
alert(this.age);
};
var instance1=new SubType("Nicholas",29);
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
instance1.sayName(); //"Nicholas"
instance1.sayAge(); //29
var instance2=new SubType("Greg",27);
alert(instance2.colors); //"red,blue,green"
instance2.sayName(); //"Greg"
instance2.sayAge(); //27
組合繼承避免了原型鏈和借用構造函數的缺陷,融合了它們的優點,成爲javascript中最常用的繼承模式。而且,instanceof和isPrototypeof()也能夠用於識別基於組合繼承創建的對象。
四、原型式繼承
藉助原型可以基於已有的對象創建新對象,同時還不必因此創建自定義類型:
function object(o){
function F(){}
F.prototype=o;
return new F();
}
ECMAScript 5 通過新增Object.create()方法規範化了原型式繼承。這個方法接受兩個參數:一個用作新對象原型的對象和(可選的)一個爲新對象定義額外屬性的對象。
var person={
name:"Nicholas",
friends:["Shelby","Court","Van"]
};
var anotherPerson=Object.create(person);
anotherPerson.name="Greg";
anotherPerson.friends.push("Rob");
var yetAnotherPerson=Object.create(person);
yetAnotherPerson.name="Linda";
yetAnotherPerson.friends.push("Barbie");
alert(person.friends); //"Shelby,Court,Van,Rob,Barbie"
Object.create()方法的第二個參數與Object.defineProperties()方法的第二個參數格式相同:每個屬性都是通過自己的描述符定義的。以這種方式指定的任何屬性都會覆蓋原型對象上的同名屬性。例如:
var person={
name:"Nicholas",
friends:["Shelby","Court","Van"]
};
var anotherPerson=Object.create(person,{
name:{
value:"Greg"
}
});
alert(anotherPerson.name); //"Greg"
缺點:
包含引用類型值的屬性始終都會共享相應的值,就像使用原型模式一樣。
五、寄生式繼承
寄生式繼承的思路與寄生構造函數和工廠模式類似,即創建一個僅用於封裝繼承過程的函數,該函數在內部以某種方式來增強對象,最後再像真的是它做了所有工作一樣返回對象。以下代碼示範了寄生式繼承模式。
function createAnother(original){
var clone=object(original); //通過調用函數創建一個新函數
clone.sayHi=function(){ //以某種方式來增強這個對象
alert("hi");
}
return clone;
}
可以像下面這樣來使用createAnother()函數:
var person={
name:"Nicholas",
friends:["Shelby","Court","Van"]
};
var antherPerson=createAnother(person);
antherPerson.sayHi; //"hi"
優點:
在主要考慮對象而不是自定義類型和構造函數的情況下,寄生式繼承也是一種有用的模式。前面示範繼承模式時使用的object()函數不是必須的;任何能夠返回新對象的函數都適用於此模式。
缺點:
使用寄生式繼承來爲對象添加函數,由於不能做到函數複用而降低效率;這一點與構造函數模式類似。
六、寄生組合式繼承
寄生組合式繼承的基本模式如下:
function inheritPrototype(subType,superType){
var prototype=object(superType.prototype); //創建對象
prototype.constructor=subType; //增強對象
subType.prototype=prototype; //指定對象
}
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);
};
開發人員普遍認爲寄生式組合繼承是引用類型最理想的繼承範式。