JS Pro-深入面向?qū)ο蟮某绦蛟O(shè)計之繼承的詳解
原型鏈(prototype chaining):
利用原型來繼承屬性和方法?;仡櫼幌聵?gòu)造函數(shù)(constructor),原型對象(prototype)和實例(instance)的關(guān)系。每一個構(gòu)造函數(shù)都有一個prototype屬性,該屬性指向一個prototype對象;prototype對象也有constructor屬性,指向該函數(shù);而實例也有一個內(nèi)部指針(__proto__)指向這個prototype對象。如果這個prototype對象是另外一個對象的實例會是怎樣的呢?這樣該prototype對象就包含一個指向另一個類型的指針,相應(yīng)地,另一個原型中也包含著一個指向另一個構(gòu)造函數(shù)的指針。
JS的繼承很簡單,就是把子類的prototype設(shè)為父類的一個(實例化)對象
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function(){
return this.property;
};
function SubType(){
this.subproperty = false;
}
//inherit from SuperType
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function (){
return this.subproperty;
};
var instance = new SubType();
alert(instance.getSuperValue()); //true
最終的結(jié)果:instance的__proto__指向SubType.prototype對象,而SubType.prototype對象的__proto__屬性又指向SuperType.prototype對象。getSuperValue()是一個方法,所以仍然存在于原型中,而property是一個實例屬性,所以現(xiàn)在存在于SubType.prototype這個實例中。 instance.constructor現(xiàn)在指向的是SuperType,這是由于SubType.prototype指向SuperType.prototype,而SuperType.prototype的constructor屬性指向SuperType函數(shù),所以instance.constructor指向SuperType。
默認情況下,所有引用類型都繼承Object。這是因為所有函數(shù)的原型對象,默認都是Object的一個實例,所以內(nèi)部prototype(__proto__)指向Object.Prototype。
原型和實例的關(guān)系:可以使用2種方法來確定原型與實例之間的關(guān)系。
- instancef操作符:使用該操作符來測試實例與原型鏈中出現(xiàn)過的構(gòu)造函數(shù),都會返回true
alert(instance instanceof Object); //true
alert(instance instanceof SuperType); //true
alert(instance instanceof SubType); //true
- isPrototypeOf()方法:只要是原型鏈中出現(xiàn)過的原型,都可以說是該原型鏈所派生的實例的原型。
alert(Object.prototype.isPrototypeOf(instance)); //true
alert(SuperType.prototype.isPrototypeOf(instance)); //true
alert(SubType.prototype.isPrototypeOf(instance)); //true
給子類添加方法的注意點:我們有的時候會給子類添加方法,或者是重寫父類的某些方法。這個時候就要注意,這些方法必須在繼承后再定義。以下的例子里,SubType在繼承SuperType后,我們給它添加了新的方法getSubValue(),而且重寫了getSuperValue()方法。對于后者,只有SubType的實例才會使用重寫的方法,SuperType的實例還是會使用原有的getSuperValue()方法。
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function(){
return this.property;
};
function SubType(){
t his.subproperty = false;
}
//inherit from SuperType
SubType.prototype = new SuperType();
//new method
SubType.prototype.getSubValue = function (){
return this.subproperty;
};
//override existing method
SubType.prototype.getSuperValue = function (){
return false;
};
var instance = new SubType();
alert(instance.getSuperValue()); //false
另外一個需要注意的是,通過原型鏈實現(xiàn)繼承時,不能使用對象字面量創(chuàng)建原型方法,因為這樣會重寫原型鏈。如下面的代碼,在SubType繼承SuperType以后,使用對象字面量給原型添加方法,但這樣做,會重寫SubType原型,重寫后的SubType.prototype包含的是一個Object的實例,從而也切斷了與SuperType的關(guān)系。
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function(){
return this.property;
};
function SubType(){
this.subproperty = false;
}
//inherit from SuperType
SubType.prototype = new SuperType();
//try to add new methods - this nullifies the previous line
SubType.prototype = {
getSubValue : function (){
return this.subproperty;
},
someOtherMethod : function (){
return false;
}
};
var instance = new SubType();
alert(instance.getSuperValue()); //error!
原型鏈的問題:和原型一樣,當(dāng)使用引用類型值的時候,原型鏈就會出問題了?;仡櫼幌轮暗膬?nèi)容,包含一個引用類型值的原型屬性會被所有實例共享,這就是為什么我們要把引用類型值在構(gòu)造函數(shù)中定義,而不是在原型中定義。在通過原型鏈實現(xiàn)繼承時,原型實際上會變成另一個類型的實例,于是,原先的實例屬性也順利成章的變成現(xiàn)在的原型屬性了。
function SuperType(){
this.colors = [“red”, “blue”, “green”];
}
function SubType(){
}
//inherit from SuperType
SubType.prototype = new 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,black”
在SuperType構(gòu)造函數(shù)中,我們定義了一個colors數(shù)組,每一個SuperType實例都會擁有它自己的這個colors數(shù)組。但是當(dāng)SubType使用原型鏈繼承SuperType以后,SubType.prototype變成SuperType的一個實例,因此它擁有自己的colors屬性,也就是說SubType.prototype.colors屬性。所以,當(dāng)創(chuàng)建SubType實例的時候,所有實例都共享這一屬性了。如上面的代碼所示。
第二個問題就是:在創(chuàng)建子類的實例時,不能向超類的構(gòu)造函數(shù)中傳遞參數(shù)。實際上,應(yīng)該說是沒有辦法在不影響所有對象實例的情況下,給超類的構(gòu)造函數(shù)傳遞參數(shù)。由于這些問題,我們不會單獨使用原型鏈。
--------------------------------------------------------------------------------
借用構(gòu)造函數(shù)(Contructor stealing):
為了解決上述問題,開發(fā)人員發(fā)明了一種叫做借用構(gòu)造函數(shù)的技術(shù)。這種技術(shù)的思路就是:在子類型構(gòu)造函數(shù)的內(nèi)部調(diào)用超類型構(gòu)造函數(shù)。(函數(shù),只不過是在特定環(huán)境中執(zhí)行代碼的對象?)我們可以通過使用apply()或call()方法在新創(chuàng)建的對象上執(zhí)行構(gòu)造函數(shù)。
function SuperType(){
this.colors = [“red”, “blue”, “green”];
}
function SubType(){
//inherit from 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里使用call()方法調(diào)用SuperType的構(gòu)造函數(shù),實際上就是在新的SubType對象上執(zhí)行SuperType()函數(shù)中定義的所有對象初始化代碼。結(jié)果就是每個SubType實例都具有自己的colors屬性的副本。
傳遞參數(shù):使用借用構(gòu)造函數(shù)方法的一個很大的好處在于就是,我們可以從子類的構(gòu)造函數(shù)傳遞參數(shù)到父類的構(gòu)造函數(shù)中。
function SuperType(name){
this.name = name;
}
function SubType(){
//inherit from SuperType passing in an argument
SuperType.call(this, “Nicholas”);
//instance property
this.age = 29;
}
var instance = new SubType();
alert(instance.name); //”Nicholas”;
alert(instance.age); //29
新的SuperType構(gòu)造函數(shù)新增了一個參數(shù)name,我們在call SuperType的同時,往SuperType傳遞參數(shù)"Nicholas"。為了不讓超類型的構(gòu)造函數(shù)重寫子類型的屬性,可以在調(diào)用超類型構(gòu)造函數(shù)后再定義子類的屬性。
借用構(gòu)造函數(shù)的問題:方法都在構(gòu)造函數(shù)中定義,無法復(fù)用。而且在超類型的原型中定義的方法,對子類型而言是不可見的。結(jié)果所有類型都只能使用構(gòu)造函數(shù)模式。
--------------------------------------------------------------------------------
組合繼承:
結(jié)合原型鏈及借用構(gòu)造函數(shù)各自的優(yōu)點的一種繼承模式。使用原型鏈繼承原型屬性及方法,使用借用構(gòu)造函數(shù)來繼承實例屬性。如下面例子,我們使用call()方法調(diào)用SuperType的構(gòu)造函數(shù)(每個SubType實例都擁有自己的name和colors屬性,以及SubType的age屬性);然后再把SuperType實例賦值給SubType的原型,使其繼承SuperType的sayName()方法(每個實例都共用這個方法)。
function SuperType(name){
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
alert(this.name);
};
function SubType(name, age){
//inherit properties
SuperType.call(this, name);
this.age = age;
}
//inherit methods
SubType.prototype = new 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
原型式繼承(Prototypal Inheritance):
function object(o){
function F(){}
F.prototype = o;
return new F();
}
--------------------------------------------------------------------------------
寄生式繼承(Parasitic Inheritance):
缺點同構(gòu)造函數(shù)
- JavaScript面向?qū)ο笾甈rototypes和繼承
- javascript 面向?qū)ο?實現(xiàn)namespace,class,繼承,重載
- javascript 面向?qū)ο笕吕砭氈屠^承
- javascript 面向?qū)ο缶幊袒A(chǔ):繼承
- javascript 面向?qū)ο笕吕砭氈^承與多態(tài)
- javascript 面向?qū)ο蠓庋b與繼承
- JS 面向?qū)ο笾^承---多種組合繼承詳解
- Javascript簡單實現(xiàn)面向?qū)ο缶幊汤^承實例代碼
- javascript 面向?qū)ο罄^承
- javaScript面向?qū)ο罄^承方法經(jīng)典實現(xiàn)
- JS實現(xiàn)面向?qū)ο罄^承的5種方式分析
相關(guān)文章
bootstrap-table實現(xiàn)表頭固定以及列固定的方法示例
這篇文章主要介紹了bootstrap-table實現(xiàn)表頭固定以及列固定的方法示例,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03js 公式編輯器 - 自定義匹配規(guī)則 - 帶提示下拉框 - 動態(tài)獲取光標(biāo)像素坐標(biāo)
這篇文章主要介紹了js公式編輯器 - 自定義匹配規(guī)則 - 帶提示下拉框 - 動態(tài)獲取光標(biāo)像素坐標(biāo)的方法,,需要的朋友可以參考下2018-01-01javascript add event remove event
javascript事件綁定和刪除功能代碼2008-04-04