原型鏈繼承
為了讓子類繼承父類的屬性(也包括方法),首先需要定義一個構造函數。然后,將父類的新實例賦值給構造函數的原型。代碼如下:
function Parent(){
this.name = 'mike';
}
function Child(){
this.age = 12;
}
Child.prototype = new Parent();//Child繼承Parent,通過原型,形成鏈條
var test = new Child();
alert(test.age);
alert(test.name);//得到被繼承的屬性
//繼續原型鏈繼承
function Brother(){ //brother構造
this.weight = 60;
}
Brother.prototype = new Child();//繼續原型鏈繼承
var brother = new Brother();
alert(brother.name);//繼承了Parent和Child,彈出mike
alert(brother.age);//彈出12
以上原型鏈繼承還缺少一環,那就是Object
,所有的構造函數都繼承自Object
。而繼承Object
是自動完成的,并不需要我們自己手動繼承,那么他們的從屬關系是怎樣的呢?
確定原型和實例的關系
可以通過兩種方式來確定原型和實例之間的關系。操作符instanceof
和isPrototypeof()
方法:
alert(brother instanceof Object)//true
alert(test instanceof Brother);//false,test 是brother的超類
alert(brother instanceof Child);//true
alert(brother instanceof Parent);//true
只要是原型鏈中出現過的原型,都可以說是該原型鏈派生的實例的原型,因此,isPrototypeof()
方法也會返回true
在js
中,被繼承的函數稱為超類型(父類,基類也行),繼承的函數稱為子類型(子類,派生類)。使用原型繼承主要由兩個問題:
- 字面量重寫原型會中斷關系,使用引用類型的原型,并且子類型還無法給超類型傳遞參數。
- 偽類解決引用共享和超類型無法傳參的問題,我們可以采用“借用構造函數”技術
借用構造函數(類式繼承)
function Parent(age){
this.name = ['mike','jack','smith'];
this.age = age;
}
function Child(age){
Parent.call(this,age);
}
var test = new Child(21);
alert(test.age);//21
alert(test.name);//mike,jack,smith
test.name.push('bill');
alert(test.name);//mike,jack,smith,bill
借用構造函數雖然解決了剛才兩種問題,但沒有原型,則復用無從談起,所以我們需要原型鏈+借用構造函數
的模式,這種模式稱為組合繼承
組合繼承
function Parent(age){
this.name = ['mike','jack','smith'];
this.age = age;
}
Parent.prototype.run = function () {
return this.name + ' are both' + this.age;
};
function Child(age){
Parent.call(this,age);//對象冒充,給超類型傳參
}
Child.prototype = new Parent();//原型鏈繼承
var test = new Child(21);//寫new Parent(21)也行
alert(test.run());//mike,jack,smith are both21
組合式繼承是比較常用的一種繼承方法,其背后的思路是 使用原型鏈實現對原型屬性和方法的繼承,而通過借用構造函數來實現對實例屬性的繼承。這樣,既通過在原型上定義方法實現了函數復用,又保證每個實例都有它自己的屬性。
原型式繼承
這種繼承借助原型并基于已有的對象創建新對象,同時還不用創建自定義類型的方式稱為原型式繼承
function obj(o){
function F(){}
F.prototype = o;
return new F();
}
var box = {
name : 'trigkit4',
arr : ['brother','sister','baba']
};
var b1 = obj(box);
alert(b1.name);//trigkit4
b1.name = 'mike';
alert(b1.name);//mike
alert(b1.arr);//brother,sister,baba
b1.arr.push('parents');
alert(b1.arr);//brother,sister,baba,parents
var b2 = obj(box);
alert(b2.name);//trigkit4
alert(b2.arr);//brother,sister,baba,parent
原型式繼承首先在obj()
函數內部創建一個臨時性的構造函數 ,然后將傳入的對象作為這個構造函數的原型,最后返回這個臨時類型的一個新實例。
寄生式繼承
這種繼承方式是把原型式+工廠模式結合起來,目的是為了封裝創建的過程。
function create(o){
var f= obj(o);
f.run = function () {
return this.arr;//同樣,會共享引用
};
return f;
}
組合式繼承的小問題
組合式繼承是js
最常用的繼承模式,但組合繼承的超類型在使用過程中會被調用兩次;
- 一次是創建子類型的時候,
- 另一次是在子類型構造函數的內部
function Parent(name){
this.name = name;
this.arr = ['哥哥','妹妹','父母'];
}
Parent.prototype.run = function () {
return this.name;
};
function Child(name,age){
Parent.call(this,age);//第二次調用
this.age = age;
}
Child.prototype = new Parent();//第一次調用
以上代碼是之前的組合繼承,那么寄生組合繼承,解決了兩次調用的問題。
寄生組合式繼承(最理想的繼承模式)
寄生式組合繼承:通過借用構造函數來繼承屬性,通過原型鏈的混成形式來繼承方法。
基本思路是:不必為了指定子類型的原型而調用超類型的構造函數,我們所需要的無非就是超類型原型的一個副本而已。本質上,使用寄生式繼承來繼承超類型的原型,然后再將結果指定給子類型的原型。
基本模式:
function object(o) { function F() {} F.prototype = o; return new F(); } function inheritPrototype(subType, superType) { var prototype = object(superType.prototype); //創建對象 prototype.constructor = subType; //增強對象 subType.prototype = prototype; //指定對象 }
這個示例中的inheritPrototype()函數實現了寄生組合式繼承的最簡單形式。
這個函數接收兩個參數:子類型構造函數和超類型構造函數。
在函數內部:
第一步是創建超類型原型的一個副本。
第二步是為創建的副本添加constructor屬性,從而彌補因重寫原型而失去的默認的constructor屬性。
最后一步,將新創建的對象賦值給子類型的原型。
這樣,我們就可以使用inheritPrototype()函數的語句,去替換前面例子中為子類型原型賦值的語句了,如:
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); } 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
這個例子的高效率體現了它只調用了一次SuperType構造函數,并且因此避免了在SubType.prototype上面創建不必要的、多余的屬性。與此同時,原型鏈還能保持不變;因此,還能夠正常使用instanceof和isPrototypeOf()。開發人員普遍認為寄生組合式繼承是引用類型最理想的繼承模式。
部分來源于JavaScript高級程序設計!若有不當之處,望不吝賜教!
文章列表