1. 程式人生 > >JavaScript實現繼承的幾種方式總結一

JavaScript實現繼承的幾種方式總結一

相同 實踐 extend sta 執行 instance () class new

雖然在ES6中有了繼承,使用extends關鍵字就能實現。本篇講的不是這種,而是ES6之前的幾種實現繼承的方式。

(一)原型鏈

ECMAScript中將原型鏈作為實現繼承的主要方法。其基本思想是利用原型讓一個引用類型繼承另一個引用類型的屬性和方法。(不理解原型鏈的童鞋們可以翻閱一下我之前的博客,裏面有詳細的說明)

實現原型鏈的一種基本模式

function SuperType(){
      this.prototype=true;
}
SuperType.prototype.getSuperValue=function(){
      return this.property;
}
function
SubType(){ this.subproperty=false; } //通過創建SuperType的實例繼承了SuperType SubType.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(){
}
//繼承了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

(二)借用構造函數

在解決原型中包含引用類型值所帶來的問題中,使用借用構造函數技術來解決。借用構造函數的基本思想,即在子類型構造函數的內部調用超類型構造函數。函數只不過是在特定環境中執行代碼的對象,因此通過使用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,black

var 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);  //mary
alert(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,black
instance1.sayName();  //mary
instance1.sayAge();  //22

var instance2=new SubType("greg", 25);
alert(instance2.colors);   //red,blue,green
instance2.sayName();  //greg
instance2.sayAge();  //25

這個例子中,兩個實例既分別擁有自己的屬性,包括colors屬性,又可以使用相同的方法。

組合繼承避免了原型鏈和借用構造函數的缺點,融合了他們的優點,是JavaScript中最常用的繼承模式。

JavaScript實現繼承的幾種方式總結一