雖然在ES6中有了繼承,使用extends關鍵字就能實現。本篇講的不是這種,而是ES6以前的幾種實現繼承的方式。app
(一)原型鏈函數
ECMAScript中將原型鏈做爲實現繼承的主要方法。其基本思想是利用原型讓一個引用類型繼承另外一個引用類型的屬性和方法。(不理解原型鏈的童鞋們能夠翻閱一下我以前的博客,裏面有詳細的說明)this
實現原型鏈的一種基本模式spa
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()則是一個原型方法。prototype
注意:原型鏈雖然很強大,能夠實現繼承,但存在兩個主要的問題。(1)包含引用類型值的原型屬性會被全部實例共享,這會致使對一個實例的修改會影響另外一個實例。code
(2)在建立子類型的實例時,不能向超類型的構造函數中傳遞參數。因爲這兩個問題的存在,實踐中不多單獨使用原型鏈。對象
如下例子清楚的說明了第一個問題blog
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()方法能夠在新建立的對象上執行構造函數。以下例子ip
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中最經常使用的繼承模式。