原型鏈繼承
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function(){
return this.property;
};
function SubType(){
this.subproperty = false;
}
// 繼承了 SuperType
SubType.prototype = new SuperType();
var subType = new SubType();
console.log(subType.getSuperValue()); // 繼承了 SuperType 的 getSuperValue 方法,列印 true
缺點
- 如果 SuperType 存在一個引用型別的屬性,而 SubType 的原型物件變為 SuperType 的一個例項,這樣每個 SubType 的例項都會共用這個引用型別的屬性,不同的 SubType 例項對該屬性的操作都將會在其它 SubType 例項中體現出來,這跟每個例項擁有自己的屬性是違背的。
function SuperType(){
this.colors = ["red", "blue", "green"];
}
function SubType(){
}
// 繼承了 SuperType
SubType.prototype = new SuperType();
var subType1 = new SubType();
subType1.colors.push("black"); // subType1 修改 colors
console.log(subType1.colors); // "red,blue,green,black"
var subType2 = new SubType();
console.log(subType2.colors); // "red,blue,green,black",subType2 的 colors 值為 subType1 修改之後的值
- 在建立子型別(SubType)的例項時,不能向超型別(SuperType)的建構函式中傳遞引數。實際上,應該說是沒有辦法在不影響所有物件例項(原因如缺點1)的情況下,給超型別的建構函式傳遞引數。
借用建構函式繼承
function SuperType(){
this.colors = ["red", "blue", "green"];
}
function SubType(){
// 繼承了 SuperType
SuperType.call(this); // 通過 apply() 或 call() 執行 SuperType 的建構函式
}
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"
該方法解決了 原型鏈繼承
的引用型屬性共享的問題。
還有可以在子型別建構函式中向超型別建構函式傳遞引數,如下例子:
function SuperType(name){
this.name = name;
}
function SubType(){
// 繼承 SuperType,並傳遞引數
SuperType.call(this, "Nicholas");
// 例項屬性
this.age = 29;
}
var subType = new SubType();
console.log(subType.name); //"Nicholas";
console.log(subType.age); //29
缺點
- 方法都在建構函式中定義,因此函式複用就無從談起了。而且,在超型別的原型中定義的方法,對子型別而言也是不可見的,結果所有型別都只能使用建構函式模式。
原型鏈/建構函式組合繼承
function SuperType(name){
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
console.log(this.name);
};
function SubType(name, age){
// 執行 SuperType 的建構函式,繼承 SuperType 的屬性
SuperType.call(this, name);
this.age = age;
}
// 將 SuperType 的例項賦給 SubType 的原型物件,這樣 SubType 可繼承 SuperType 原型中的方法
SubType.prototype = new SuperType();
SubType.prototype.sayAge = function(){
console.log(this.age);
};
var subType1 = new SubType("Nicholas", 29);
subType1.colors.push("black");
console.log(subType1.colors); // "red,blue,green,black"
subType1.sayName(); // "Nicholas";
subType1.sayAge(); // 29
var subType2 = new SubType("Greg", 27);
console.log(subType2.colors); // "red,blue,green"
subType2.sayName(); // "Greg";
subType2.sayAge(); // 27
缺點
- 無論什麼情況,都會呼叫兩次超型別建構函式,一次是在建立子型別原型的時候,一次是在執行子型別建構函式的時候。這樣一個 SubType 例項會包含兩組 SuperType 的屬性,一組在 SubType 例項上,一組在 SubType 原型中。
原型式繼承
該方法基於已有的物件建立新物件,同時還不必因此建立自定義型別。
var person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = Object.create(person); // 使用 person 作為新物件(anotherPerson)的原型
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");
var yetAnotherPerson = Object.create(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");
console.log(person.friends); // "Shelby,Court,Van,Rob,Barbie"
寄生式繼承
該方法建立一個僅用於封裝繼承過程的函式,該函式在內部以某種方式來增強物件,最後再像真地是它做了所有工作一樣返回物件。
function creatAnother(original) {
var clone = Object.create(original); // 建立一個新物件
clone.sayHi = function() { // 以某種方式來增強這個物件
console.log("hi");
}
return clone; // 返回該物件
}
// 使用示例
var person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = creatAnother(person);
anotherPerson.sayHi(); // "hi"
寄生/原型鏈/建構函式組合繼承
該方法解決原型鏈/建構函式組合繼承呼叫兩次超型別建構函式的問題。
通過借用建構函式來繼承屬性,通過原型鏈的混成形式來繼承方法。其背後的基本思路是:不必為了指定子型別的原型而呼叫超型別的建構函式,我們所需要的無非就是超型別原型的一個副本而已。本質上,就是使用寄生式繼承來繼承超型別的原型,然後再將結果指定給子型別的原型。
function object(o){
function F(){}
F.prototype = o;
return new F();
}
function inheritPrototype(subType, superType){
var prototype = object(superType.prototype); // 根據超型別原型建立新物件
prototype.constructor = subType; // 將新物件的 constructor 設定為子型別
subType.prototype = prototype; // 將新物件賦給子型別的原型
}
function SuperType(name){
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
console.log(this.name);
};
function SubType(name, age){
SuperType.call(this, name);
this.age = age;
}
inheritPrototype(SubType, SuperType);
SubType.prototype.sayAge = function(){
console.log(this.age);
};
var subType1 = new SubType("Nicholas", 29);
subType1.colors.push("black");
console.log(subType1.colors); // "red,blue,green,black"
subType1.sayName(); // "Nicholas";
subType1.sayAge(); // 29
var subType2 = new SubType("Greg", 27);
alert(subType2.colors); // "red,blue,green"
subType2.sayName(); // "Greg";
subType2.sayAge(); // 27
參考資料:《JavaScript高階程式設計(第3版)》第6.3節 繼承