1. 程式人生 > >重學JavaScript之面向物件的程式設計(繼承)

重學JavaScript之面向物件的程式設計(繼承)

1. 繼承

ES 中只支援實現繼承,而且其實現繼承主要依靠原型鏈來實現的。

2. 原型鏈

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

回顧一下建構函式、原型和例項的關係

每個建構函式都有一個原型物件,原型物件都包含一個指向建構函式的指標,而例項都包含一個指向原型物件的內部指標。那麼假如我們讓原型物件等於另一個型別的例項。那麼此時的原型物件將包含一個指向另一個原型的指標,相應地,另一個原型中也包含著一個指向另一個建構函式的指標。

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

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實現的。實現的本質是重寫原型物件,代之以一個新型別的例項。

也就是說,原來存在於SuperType的例項中的所有方法和屬性,現在也存在於SubType.prototype中。在給SubType.prototype新增一個方法後,這樣就在繼承了SuperType的屬性和方法的基礎上又添加了一個新的方法。這樣就實現了例項以及建構函式和原型之間的關係。

通過實現原型鏈,本質上擴充套件了原型搜尋機制,當以讀取模式訪問一個例項屬性時,首先會在例項中搜索該屬性。如果沒有找到該屬性,則會繼續搜尋例項的原型。在通過原型鏈實現繼承的情況下,搜尋過程就得以沿著原型鏈繼續向上。直到最後一步找到該方法。在找不到屬性和方法的情況下,搜尋過程總是要一環一環地前行到原型鏈末端才會停下來。

3. 預設的原型

所有引用型別預設都繼承了Object,而這個繼承也是通過原型鏈實現的。所有函式的預設原型都是Object的例項 因此預設原型都會包含一個內部指標,指向 Object.prototype。這也是所有自定義型別都會繼承 toString()、valueOf()預設方法的原因

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. 原型鏈的問題

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

1、來自包含引用型別值的原型。在之前說過包含引用型別值的原型屬性會被所有例項共享。所以這也是為什麼要在建構函式中,而不是在原型物件中定義屬性的原因。在通過原型來實現繼承時,原型實際上會變成另一個型別的例項。於是,原先的例項屬性也就順理成章地變成了現在的原型屬性。

2、在建立子型別的例項時,不能向超型別的建構函式中傳遞引數。實際上可以說是沒有辦法再不影響所有物件例項的情況下,給超型別的建構函式傳遞引數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 寄生組合式繼承

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

關注公眾號 【小夭同學】

相關推薦

JavaScript面向物件程式設計繼承

1. 繼承 ES 中只支援實現繼承,而且其實現繼承主要依靠原型鏈來實現的。 2. 原型鏈 ES中 描述了 原型鏈的概念,並將原型鏈作為實現繼承的主要方法。其基本思想是利用原型讓一個引用型別繼承另一個引用型別的屬性和方法。 回顧一下建構函式、原型和例項的關係 每個建構函式都有一個原型物件,原型物件

JavaSE面向物件程式設計

Java語言的三大特性:平臺無關係、安全性、網路移動性。Java是面向物件的程式語言,Java的三大特性與面向物件的封裝、繼承、多型、抽象有著千絲萬縷的關係。 1.封裝 封裝是一種資訊隱蔽技術,就是把屬性私有化,提供公共方法訪問私有物件。封裝還指把物件的屬性和行為看成是一個密不可分的整體。

聰哥哥教你Python面向物件程式設計

什麼是面向物件程式設計? 引用百度百科解釋: 面向物件程式設計(Object Oriented Programming)作為一種新方法,其本質是以建立模型體現出來的抽象思維過程和麵向物件的方法。模型是用來反映現實世界中事物特徵的。任何一個模型都不可能反映客觀事物的一切具體特徵,只能對事物特徵

泰/王志成《面向物件程式設計java》第十一週學習總結

第一部分:理論知識學習部分 第十一章理論知識主要為集合類的介紹,在實驗中都有所體現且本週主要複習回顧上週的泛型程式設計 第二部分:實驗部分 ——集合 1、實驗目的與要求 (1) 掌握Vetor、Stack、Hashtable三個類的用途及常用API; (2) 瞭解jav

王志成/王泰《面向物件程式設計java》第十一週學習總結

理論學習部分: JAVA的集合框架 l JAVA的集合框架實現對各種資料結構的封裝,以降低對資料管理與處理的難度。 l 所謂框架就是一個類庫的集合,框架中包含很多超類,程式設計者建立這些超類的子類可較方便的設計設計程式所需的類。例如:Swing類包 l 集合(Collection或稱為容

泰201771010131《面向物件程式設計java》第十二週學習總結

第一部分:理論知識學習部分 第10章 圖形程式設計 10.1 AWT與Swing簡介 1.使用者介面(User Interface) 的概念:使用者與計算機系統(各種程式)互動的介面2.圖形使用者介面(Graphical User Interface)的概念: 以圖形方式呈現的使用者介面 3.AWT:

泰201771010131《面向物件程式設計java》第十四周學習總結

第一部分:理論知識學習部分 第12章 Swing使用者介面元件 12.1.Swing和MVC設計模式 a 設計模式初識b 模型—檢視—控制器模式c Swing元件的模型—檢視—控制器分析   12.2佈局管理器 a 佈局管理器是一組類。 b 實現java.awt.LayoutMana

Web前端學習筆記——JavaScript面向物件程式設計

JavaScript 高階 基本概念複習 由於 JavaScript 高階還是針對 JavaScript 語言本身的一個進階學習,所以在開始之前我們先對以前所學過的 JavaScript 相關知識點做一個快速複習總結。 重新介紹 JavaScript J

Javascript 面向物件程式設計:封裝

Javascript是一種基於物件(object-based)的語言,你遇到的所有東西幾乎都是物件。但是,它又不是一種真正的面向物件程式設計(OOP)語言,因為它的語法中沒有class(類)。 那麼,如果我們要把"屬性"(property)和"方法"(method),封裝成

泰 201771010131《面向物件程式設計java》第十六週學習總結

    第一部分:理論知識學習部分 第14章 併發 ⚫ 執行緒的概念⚫ 中斷執行緒⚫ 執行緒狀態⚫ 多執行緒排程⚫ 執行緒同步   1.程式與程序的概念 1.1程式是一段靜態的程式碼,它是應用程式執行的藍 本。 1.2程序是程式的一次動態執行,它

泰201771010131《面向物件程式設計java》第十七週學習總結

第一部分:理論知識學習部分 第14章 併發 執行緒同步 多執行緒併發執行不確定性問題解決方案:引入線 程同步機制,使得另一執行緒要使用該方法,就只 能等待。 ⚫ 在Java中解決多執行緒同步問題的方法有兩種: 1.- Java SE 5.0中引入ReentrantLock類(P648頁)。 2.-

泰《面向物件程式設計java》課程學習總結

第一部分:理論知識學習部分 總複習綱要 1. Java語言特點與開發環境配置(第1章、第2章) 2. Java基本程式結構(第3章) 3. Java面向物件程式結構(第4章、第5章、第6章) 4. 類、類間關係、類圖 5. Java JDK預定義類/介面及其API(String-第3章、 Arr

Python學習==>面向物件程式設計

一、類的特殊成員 我們在 Python學習之==>面向物件程式設計(一)中已經介紹過了構造方法和析構方法,構造方法是在例項化時自動執行的方法,而析構方法是在例項被銷燬的時候被執行,Python類成員中還存在著一些具有特殊意義的方法,下面我們來一一介紹一下: 1、__doc__ 表示類的描述資訊

JavaScript面向對象ECMAScript5

知識 參考 sse 無序 原型對象 把他 lap gree 同時 理解對象屬性 創建對象 繼承 理解對象屬性 ECMA-262稱對象為:無序屬性的集合,其屬性可以包含基本值,對象或者函數。 由此在ECMAScript中可以把對象想象成散列表,無非就是鍵值對,值可以為

楊玲 201771010133《面向物件程式設計java》第十週學習總結

《面向物件程式設計(java)》第十週學習總結 第一部分:理論知識學習部分    第八章    泛型程式設計 一、泛型程式設計的定義 1、JDK 5.0 中增加的泛型型別,是Java 語言中型別安全的一次重要改進。 2、 泛型:也稱引數化型別(paramete

徐思201771010132《面向物件程式設計java》第十週學習總結

一、理論知識部分 泛型:也稱引數化型別,就是在定義類、介面和方法時,通過型別引數指示將要處理的物件型別。(如ArrayList類) 泛型程式設計(Generic programming):編寫程式碼可以被很多不同型別的物件所重用。 一個泛型類(generic class)就是具有一個或多個型別變數的類,

劉志梅 2017710101152《面向物件程式設計java》 第十週學習總結

實驗十  泛型程式設計技術 實驗時間 2018-11-1 1、實驗目的與要求 (1)泛型程式設計:意味著編寫的程式碼可以被很多不同型別的物件所重用。(ArrayList類可以聚集任何型別的物件) 如果在其它地方,若將get的結果強制型別轉換為String型別,就會產生一個錯誤;泛型

201771010120 蘇浪浪 面向物件程式設計Java第10周

  1、實驗目的與要求 (1) 理解泛型概念; (2) 掌握泛型類的定義與使用; (3) 掌握泛型方法的宣告與使用; (4) 掌握泛型介面的定義與實現; (5)瞭解泛型程式設計,理解其用途。 2、實驗內容和步驟 實驗1: 匯入第8章示例程式,測試程式並進行程式碼註釋。 測試程式1:

201771010128王玉蘭《面向物件程式設計Java》第十週學習總結

第一部分:理論知識部分總結: (1) 定義簡單泛型類: A:泛型:也稱引數化型別(parameterizedtype),就是在定義類、介面和方法時,通過型別引數指 示將要處理的物件型別。 B:泛型程式設計(Genericprogramming):編寫 程式碼可以被很多不同型別的物件所重用。 C: 一個

201771010112羅鬆《面向物件程式設計java》第十週學習總結