重學JavaScript之面向對象的程序設計 => 繼承

1. 繼承

ES 中只支持實現繼承,並且其實現繼承主要依靠原型鏈來實現的。編程

2. 原型鏈

ES中 描述了 原型鏈的概念,並將原型鏈做爲實現繼承的主要方法。其基本思想是利用原型讓一個引用類型繼承另外一個引用類型的屬性和方法。bash

回顧一下構造函數、原型和實例的關係

每一個構造函數都有一個原型對象,原型對象都包含一個指向構造函數的指針,而實例都包含一個指向原型對象的內部指針。那麼假如咱們讓原型對象等於另外一個類型的實例。那麼此時的原型對象將包含一個指向另外一個原型的指針,相應地,另外一個原型中也包含着一個指向另外一個構造函數的指針。app

另外,假如另外一個原型又是另外一個類型的實例,如此層層遞進,就構成了實例與原型的鏈條。這就是原型鏈的基本概念。函數

function SuperType(){
    this.property = true
}

SuperType.prototype.getSuperValue = function(){
    retrun this.property
}

function SubType(){
    this.subproperty = false
}

// 繼承了 SuperType
SubType.prototype = new SuperType()
SubType.prototype.getSubValue = function(){
    retrun this.subproperty;
}

var instance = new SubType()
instance.getSuperValue()    // true
複製代碼
上面代碼中定義了兩個類型:

SuperType 和SubType。每一個類型分別有一個屬性和一個方法。它們的主要區別是:測試

SubType繼承了SuperType,而繼承是經過建立SuperType的實例,並將該實例賦給SubType.prototype實現的。實現的本質是重寫原型對象,代之以一個新類型的實例。ui

也就是說,原來存在於SuperType的實例中的全部方法和屬性,如今也存在於SubType.prototype中。在給SubType.prototype添加一個方法後,這樣就在繼承了SuperType的屬性和方法的基礎上又添加了一個新的方法。這樣就實現了實例以及構造函數和原型之間的關係。this

經過實現原型鏈,本質上擴展了原型搜索機制,當以讀取模式訪問一個實例屬性時,首先會在實例中搜索該屬性。若是沒有找到該屬性,則會繼續搜索實例的原型。在經過原型鏈實現繼承的狀況下,搜索過程就得以沿着原型鏈繼續向上。直到最後一步找到該方法。在找不到屬性和方法的狀況下,搜索過程老是要一環一環地前行到原型鏈末端纔會停下來。spa

3. 默認的原型

全部引用類型默認都繼承了Object,而這個繼承也是經過原型鏈實現的。全部函數的默認原型都是Object的實例 所以默認原型都會包含一個內部指針,指向 Object.prototype。這也是全部自定義類型都會繼承 toString()、valueOf()默認方法的緣由prototype

4. 原型和實例的關係

能夠經過兩種方式來肯定原型和實例之間的關係。第一種方式就是使用 instanceof操做符,只要用這個操做符來測試實例與原型鏈中出現過的構造函數,結果就會返回 true。以下:指針

instance instanceof Object  // true
複製代碼

第二種方法就是 isPrototypeOf() 方法,一樣,只要原型鏈中出現過的原型,均可以說是該原型鏈所派生的實例原型,所以 isPrototypeOf() 方法也會返回 true

Object.prototype.isPrototypeOf(instance)    // true
複製代碼

5. 謹慎地定義方法

子類型有時候須要重寫超類型中的某個方法,或者須要添加超類型中不存在的某個方法,可是不管如何,給原型添加方法的代碼必定要放在替換原型的語句以後

function  SuperType() {
    this.property = true
}

SuperType.prototype.getSuperValue = function(){
    retrun this.property
}

function SubType() {
    this.subproperty = false
}
----
// 繼承了SuperType
SubType.prototype = new SuperType()

// 添加新方法
SubType.prototype.getSubValue = function(){
    retrun this.subproperty
}
//重寫超類型中的方法
SubType.prototype.getSuperValue = function(){
    retrun false
}
-----
var instance = new SubType()
instance.getSuperValue()    // false
複製代碼
以上代碼中分隔的部分是兩個方法的定義。

第一個方法 getSubValue()被添加到了SubType中,第二個方法 getSuperValue()是原型鏈中已經存在的一個方法,但重寫這個方法將會屏蔽原來那個方法.

換句話說,當 SubType 的實例調用 getSuperValue()時,調用的就是這個從新定義的方法,但經過 SuperType的實例調用 getSuperValue()時,還會繼續調用原來的那個方法,全部必需要在用SuperType的實例替換原型以後,在定義這兩個方法。

注意:即在經過原型鏈實現繼承的時候,不能使用對象字面量建立原型方法,由於這樣作會重寫原型。

6. 原型鏈的問題

原型鏈雖然很強大,能夠用它來實現繼承,但也存在必定的問題。

一、來自包含引用類型值的原型。在以前說過包含引用類型值的原型屬性會被全部實例共享。因此這也是爲何要在構造函數中,而不是在原型對象中定義屬性的緣由。在經過原型來實現繼承時,原型實際上會變成另外一個類型的實例。因而,原先的實例屬性也就瓜熟蒂落地變成了如今的原型屬性。

二、在建立子類型的實例時,不能向超類型的構造函數中傳遞參數。實際上能夠說是沒有辦法再不影響全部對象實例的狀況下,給超類型的構造函數傳遞參數a。

7. 借用構造函數

利用在子類型構造函數的內部調用超類型構造函數。便可以經過 apply() 和 call()方法在新建立的對象上執行構造函數。

7.1 傳遞參數

相對於原型鏈而言,借用構造函數有一個很大的優點,便可以在子類型構造函數中向超類型構造函數傳遞參數。

function s(name) {
    this.name = name
}

function b() {  
    // 繼承 s,同時還傳遞參數
    s.call(this, 'nnn')
    // 實例屬性
    this.age = 23
}

let i = new B()

i.name // nn
i.age  // 29
複製代碼

7.2 借用構造函數的問題

若是僅僅是借用構造函數,那麼也就沒法避免構造函數模式存在的問題,方法都在構造函數中定義,所以函數複用就沒法實現。另外,在超類型的原型中定義的方法,對子類型而言也是不可見的。結果全部類型都只能使用構造 函數模式。因此借用構造函數模式不多單獨使用。

8. 組合繼承

也叫作僞 經典繼承,指的是將原型鏈和借用構造函數的技術組合到一塊。發揮兩者的長處的一種繼承模式。原理就是使用原型鏈實現對原型屬性和方法的繼承,而經過借用構造函數來實現對實例屬性的繼承。

9. 原型式繼承

藉助已有的對象建立新對象,先建立一個臨時的構造函數,而後將傳入的對象做爲這個構造函數的原型,最後返回這個臨時類型的新實例。

10. 寄生式繼承

建立一個僅用於封裝繼承過程的函數,該函數在內部以某種方式加強對象,而後再返回對象。一樣也是不能作到函數複用而會下降效率

11. 總結

ES 支持面向對象編程,但不使用類或接口。對象能夠在代碼執行過程當中建立和加強,所以具備動態性而非嚴格定義的實體。在沒有類的狀況下,能夠採用 工廠模式、構造函數模式、原型模式建立對象。

11.1 工廠模式

使用簡單的函數建立對象,爲對象添加屬性和方法,而後返回對象。這個模式被構造函數模式所取代

11.2 構造函數模式

建立自定義引用類型,能夠像建立內置對象實例同樣使用 new 操做符。不過,構造函數模式也有缺點,即它的每一個成員沒法獲得複用,包括函數。因爲函數能夠不侷限於任何對象。所以沒有理由再也不多個對象之間共享函數。

11.3 原型模式

使用構造函數的 prototype 屬性來指定那些應該共享的屬性和方法。組合使用構造函數模式和原型模式時,使用構造函數定義實例屬性,而使用原型定義共享的屬性和方法。

JS主要經過原型鏈實現繼承。原型鏈的構建是將一個類型的實例賦值給另外一個構造函數的原型實現。這樣,子類型就可以訪問超類型的全部屬性和方法。這點和基於類的繼承很類似。

原型鏈的問題就是對象實例共享全部繼承的屬性和方法,所以不適合單獨使用。 若是想解決這個問題就須要藉助於構造函數,即在子類型構造函數的內部調用超類型構造函數。這樣就能夠作到每一個實例都具備本身的屬性,同時還能保證只使用構造函數模式來定義類型。

11.4 原型式繼承

能夠在不預先定義構造函數的狀況下實現繼承,其本質是執行對給定對象的淺複製。而複製獲得的副本還能夠進一步改造

11.5 寄生式繼承

與原型式繼承很是類似,也是基於某個對象或某些信息建立一個對象,而後加強對象,最後返回對象。爲了解決組合繼承模式因爲屢次調用超類型構造函數而致使低效率問題,能夠將這個模式和組合繼承一塊兒使用

11.6 寄生組合式繼承

集寄生式繼承和組合繼承的優勢於一身,是實現基於類型繼承的最有效方式

關注公衆號 【小夭同窗】

公衆號小夭同窗
相關文章
相關標籤/搜索