圖解js的繼承

fullyouth發表於2020-12-06

繼承

github地址

1. 概念

  • 繼承是物件導向程式設計的基石。
  • 繼承就是子類繼承父類的特徵和行為,使得子類物件(例項)具有父類的屬性和方法

1.1 繼承的優點:

  • 提取公共程式碼,減少程式碼重複性
  • 提高程式碼可維護性
  • 讓類與類之間產生了關係,是多型的前提

1.2 繼承的型別:

  • 單繼承
  • 多繼承
  • 不同類繼承同一個類
  • 多繼承

2. js的繼承

js本身最開始的設計只是為了實現網頁提交表單時做個表單驗證等簡單功能
現在web端越來越重,導致js不得不持續更新完善自己,來支援實現越來越複雜的需求
所以經常會看到一些功能是有很多種實現方式的,例如非同步解決方案,都是前輩們一步一步探索更優的解決方案的過程。

js不支援多繼承
js只支援實現繼承,不支援介面繼承

下面是這張圖是js繼承的演變過程圖(對理解js的繼承,非常具有參考價值)
在這裡插入圖片描述

3. 原型鏈繼承

3.1 基本思想

通過原型繼承多個引用型別的屬性和方法

3.2 程式碼實現

function Super(address){
  this.address = address
}
Super.prototype.sayAddress = function () {
  console.log(this.address)
}

function Sub() {

}
Sub.prototype = new Super('上海')

3.3 畫圖

在這裡插入圖片描述

優缺點

優點

  • 可以實現子類繼承父類的例項屬性和原型屬性

缺點

  • 子類的原型是父類的例項,那麼父類的原型與子類是共享的(引用關係),導致父類的原型修改後,子類也會被影響
  • 子類例項化時不能給父類的建構函式傳參

4. 盜用建構函式

4.1 基本思想

為了解決原型包含引用值導致的繼承問題,出現了盜用建構函式繼承。
思想:在子類的建構函式中呼叫父類的建構函式

4.2 程式碼實現

function Super(address){
  this.address = address
}

function Sub(name) {
  Super.call(this, '上海')
  this.name = name
}

4.3 畫圖

在這裡插入圖片描述

4.4 優缺點

優點

不在利用原型鏈做繼承,而是直接呼叫父類建構函式,實現例項屬性繼承

缺點

  • 子類不能訪問父類原型上的方法,因為如果要繼承,必須所有的型別都使用建構函式模式。
  • 也是建構函式模式的問題:必須在建構函式中定義方法,函式不能重用

5. 組合繼承

5.1 基本思路

綜合了原型鏈繼承盜用建構函式繼承
思路:使用原型鏈繼承原型屬性,使用建構函式繼承例項屬性

5.2 程式碼實現

function Super(address){
  this.address = address
}
Super.prototype.sayAddress = function () {
  console.log(this.address)
}

function Sub(name) {
  Super.call(this, '上海')
  this.name = name
}
Sub.prototype = new Super()

5.3 畫圖

在這裡插入圖片描述

5.4 優缺點

優點

  • 綜合了 原型鏈繼承 和 盜用建構函式繼承 的優點
  • 保留了instanceofisPrototypeOf判斷型別的識別能力

缺點

  • 原型鏈繼承 和 盜用建構函式繼承 重複呼叫了父類建構函式

組合模式是javascript中經常使用的繼承模式,已經能夠完成正常的繼承。
但是重複呼叫建構函式是一種效能浪費

6. 原型式繼承

6.1 基本思路

出發點: 即使不建立型別,也可以實現物件之間的資訊共享
基本思路:構造一個臨時建構函式,將傳入的物件賦值給此臨時建構函式的原型,然後返回一個臨時建構函式的例項

6.2 程式碼實現

function object(o) {
  function F() {}
  F.prototype = o;
  return new F()
}

es6 通過 Object.create() 將此模式概念化了

6.3 畫圖

6.4 優缺點

優點

  • 不需要建立型別即可實現物件間資訊共享

缺點

  • 共享的資訊是引用關係,所以如果修改某一個,會彼此影響(同原型鏈繼承類似)

在這裡插入圖片描述

7. 寄生式繼承

7.1 基本思路

原型式繼承的增強,建立一個實現繼承的函式,以某種方式增強物件,然後返回這個物件。

7.2 程式碼實現

function createAnother(o) {
  let clone = object(o) // 原型式繼承
  clone.sayHi = function() {

  }
  return clone
}

8. 寄生式組合繼承

8.1 基本思路

出發點:解決組合繼承的效率問題
基本思路:不通過建構函式給子類原型賦值,而是取得父類原型的一個副本
寄生式繼承(原型屬性) + 盜用建構函式繼承(例項屬性)

8.2 程式碼實現

function inheritPrototype(sub, super) {
  // 對原型使用寄生模式,因為寄生模式最適合兩個物件之間的資訊共享
  let prototype = object(super.prototype) 
  prototype.constructor = sub
  sub.prototype = prototype
}

function Super(address){
  this.address = address
}
Super.prototype.sayAddress = function () {
  console.log(this.address)
}

function Sub(name) {
  // 盜用建構函式模式,實現例項屬性的繼承
  Super.call(this, '上海')
  this.name = name
}
inheritPrototype(Sub, Super)

8.3 畫圖

在這裡插入圖片描述

8.4 優缺點

優點

  • 最高效的實現了 例項屬性 和 原型屬性 的繼承
  • instanceof操作符和 isPrototypeOf() 方法正常有效

寄生式組合繼承可以
算是引用型別繼承的最佳模式

總結

  • 原型式繼承可以無須明確定義建構函式而實現繼承,本質上是對
    給定物件執行淺複製。這種操作的結果之後還可以再進一步增
    強。
  • 與原型式繼承緊密相關的是寄生式繼承,即先基於一個物件建立
    一個新物件,然後再增強這個新物件,最後返回新物件。這個模
    式也被用在組合繼承中,用於避免重複呼叫父類建構函式導致的
    浪費。
  • 寄生組合繼承被認為是實現基於型別繼承的最有效方式
    inherit

相關文章