標籤:例子 參數 類型 script pre 技術 對象 這一 組合
雖然在ES6中有了繼承,使用extends關鍵字就能實現。本篇講的不是這種,而是ES6之前的幾種實現繼承的方式。
(一)原型鏈
ECMAScript中將原型鏈作為實現繼承的主要方法。其基本思想是利用原型讓一個參考型別繼承另一個參考型別的屬性和方法。(不理解原型鏈的童鞋們可以翻閱一下我之前的部落格,裡面有詳細的說明)
實現原型鏈的一種基本模式
function SuperType(){ this.prototype=true;}SuperType.prototype.getSuperValue=function(){ return this.property;}function SubType(){ this.subproperty=false;}//通過建立SuperType的執行個體繼承了SuperTypeSubType.prototype=new SuperType();SubType.prototype.getSubValue=function(){ return this.subproperty;}var instance=new SubType();alert(instance.getSuperValue()); //true
上面的例子中,instance指向SubType的原型,SubType的原型又指向SuperType的原型。getSuperValue()方法仍然還在SuperType.prototype中,但property則位於SubType.prototype中。這是因為prototype是一個執行個體屬性,而getSuperValue()則是一個原型方法。
注意:原型鏈雖然很強大,可以實現繼承,但存在兩個主要的問題。(1)包含參考型別值的原型屬性會被所有執行個體共用,這會導致對一個執行個體的修改會影響另一個執行個體。
(2)在建立子類型的執行個體時,不能向超類型的建構函式中傳遞參數。由於這兩個問題的存在,實踐中很少單獨使用原型鏈。
以下例子清楚的說明了第一個問題
function SuperType(){ this.colors=["red", "blue", "green"];}function SubType(){}//繼承了SuperTypeSubType.prototype=new SuperType();var instance1=new SubType();instance1.colors.push("black");alert(instance1.colors); //red,blue,green,blackvar instance2=new SubType();alert(instance2.colors); //red,blue,green,black
(二)借用建構函式
在解決原型中包含參考型別值所帶來的問題中,使用借用建構函式技術來解決。借用建構函式的基本思想,即在子類型建構函式的內部調用超類型建構函式。函數只不過是在特定環境中執行代碼的對象,因此通過使用apply()和call()方法可以在新建立的對象上執行建構函式。如下例子
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,bllue,green,blackvar instance2=new SubType();alert(instance2.colors); //red,blue,green
上面例子中,通過使用call()方法(或者apply()方法),在新建立的SubType執行個體的環境下調用了SuperType建構函式。這樣一來就會在新的SubType對象上執行SuperType()函數中定義的所有對象初始化代碼。結果,SubType的每個執行個體都會有自己的colors屬性副本。
相對於原型鏈而言,借用建構函式可以在子類型建構函式中向超類型建構函式傳遞參數。如下例子
function SuperType(name){ this.name=name;}function SubType(){ //繼承了SuperType,同時還傳遞了參數 SuperType.call(this,"mary"); //執行個體屬性 this.age=22;}var instance=new SubType();alert(instance.name); //maryalert(instance.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("mary", 22);instance1.colors.push("black");alert(instance1.colors); //red,blue,green,blackinstance1.sayName(); //maryinstance1.sayAge(); //22var instance2=new SubType("greg", 25);alert(instance2.colors); //red,blue,greeninstance2.sayName(); //greginstance2.sayAge(); //25
這個例子中,兩個執行個體既分別擁有自己的屬性,包括colors屬性,又可以使用相同的方法。
組合繼承避免了原型鏈和借用建構函式的缺點,融合了他們的優點,是JavaScript中最常用的繼承模式。
JavaScript實現繼承的幾種方式總結一