【JS系列】繼承的這6種方式!(上)

寫在前面

繼承的簡介

  「繼承」是JavaScript面向對象設計的重要一環,願你認真讀完本文,吃透繼承的概念。app

繼承的核心

  在js中,繼承主要是依靠原型鏈實現的,原型鏈是通往繼承的必經之路。你能夠參考《一張圖完全KO原型鏈(prototype,__proto__)》這篇博文,相信它必定能很好地幫助你原型鏈。函數

1. 繼承方式一:原型鏈

1.1 介紹

  原型鏈是實現繼承最原始的模式,即經過prototype屬性實現繼承。post

//父級-構造函數
function Father() {
  this.fatherProp = true
}

//父級-原型屬性
Father.prototype.getFatherValue = function() {
  return this.fatherProp
}

//子級-構造函數
function Son() {
  this.sonProp = false
}

//子級-原型屬性:繼承父級
//即__proto__指向父級的prototype
//若不理解請閱讀《一張圖完全KO原型鏈(prototype,__proto__》
Son.prototype = new Father()

//子級-添加原型方法
Son.prototype.getSonValue = function() {
  return this.sonProp
}

//建立子級的實例對象
var son = new Son()
console.log(son.getFatherValue()) //true
複製代碼

1.2 解析:son實例對象是如何找到getFatherValue()方法的呢?

  1. 首先在son對象自身中找。若對象自身沒找到
  2. 而後在Son.prototype中找。若Son.prototype中沒找到
  3. 繼續往上一層,Son.prototype.__proto__(Fater.prototype)
  4. 依次類推,直到找到須要的屬性或方法,或到達原型鏈頂端Object.prototype

  若是到最後都沒找到,會發生什麼呢?學習

//一個不存在的方法
console.log(son.getValue()) //ERROE:not a function
複製代碼

1.3 注意事項

  1. 重寫父級原型鏈的方法或者添加父級原型鏈不存在的方法,必須在父級原型鏈代碼以後。(這個很好理解,不放代碼演示了)ui

  2. 經過原型鏈實現繼承後,不能再使用字面量的方式建立原型對象,由於會覆蓋原型鏈。this

//子級-原型屬性:繼承父級
Son.prototype = new Father()

//不能像下面這樣,這樣會使得上面的代碼無效
//由於這至關於從新建立了一個原型對象
Son.prototype = {
  getSonValue: function() {
    return this.sonProp
  }
}
複製代碼

1.4 原型鏈實現繼承的弊端

   世間萬事萬物都不可能十全而十美,原型鏈雖然強大,但也存在缺陷。spa

  1. 原型鏈中引用類型的屬性會被全部實例共享的,即全部實例對象使用的是同一份數據,會相互影響。
function Father() {
    this.arr = [1,2,3]
  }

  function Son() {
  }

  Son.prototype = new Father()

  var son1 = new Son()
  console.log(son1.arr)  //1,2,3

  var son2 = new Son()
  son2.arr.push(4)

  console.log(son2.arr)  //1,2,3,4
  console.log(son1.arr)  //1,2,3,4
複製代碼
  1. 沒法向父級構造函數傳參

2. 繼承方式二:借用構造函數

2.1 介紹

  方式一中引用類型帶來的問題可借用構造函數的方式解決。其核心思想是:在子級構造函數中調用父級構造函數。prototype

  如何實如今一個構造函數中調用另外一個函數?——call()和apply()設計

function Father() {
    this.arr = [1,2,3]
  }

  function Son() {
    //call的第一個函數是this指向的對象,即構造函數的實例對象
    Father.call(this)

    /*上面代碼等同於下面這段代碼: (function() { this.arr = [1,2,3] }).call(this) */
  }

  var son1 = new Son()
  console.log(son1.arr)  //1,2,3

  var son2 = new Son()
  son2.arr.push(4)

  console.log(son2.arr)  //1,2,3,4
  console.log(son1.arr)  //1,2,3
複製代碼
//解決傳參問題:
function Father(name) {
  this.name = name
}

function Son(name) {
  Father.call(this, name)
}

var son1 = new Son("小名")
console.log(son1.name)    //小名

var son2 = new Son("一燈")
console.log(son2.name)    //一燈
複製代碼

2.2 借用構造函數的缺陷

  這種方式是經過構造函數實現的,固然也把構造函數自身的問題帶過來了——破壞了複用性。由於每一個實例都建立了一份副本。code

3. 組合繼承

3.1 介紹

  組合繼承 = 原型鏈 + 借用構造函數。取其長避其短:共享的用原型鏈,各自的借用構造函數

function Father(name) {
  this.name = name
  this.arr = [1,2,3]
}

Father.prototype.getName = function() {
  console.log(this.name)
}

function Son(name, age) {
  Father.call(this, name)
  this.age = age
}

Son.prototype = new Father()
Son.prototype.constructor = Son
Son.prototype.getAge = function() {
  console.log(this.age)
}

var son1 = new Son("小名", 23)
son1.arr.push(4)
console.log(son1.arr) //1,2,3,4
son1.getName()        //小名
son1.getAge()         //23

var son2 = new Son("一燈", 24)
console.log(son2.arr) //1,2,3
son1.getName()        //一燈
son1.getAge()         //24
複製代碼

3.2 解析

  1. 借用構造函數部分:

  Father.call(this, name)——name來自Father

  this.age = age; Son.prototype.constructor = Son——age來自Son

  1. 原型鏈部分:

  Father.prototype.getName——getName方法來自Father.prototype

  Son.prototype.getAge——getAge來自Son.prototype

後記

  繼續學習繼承的後三種方式

相關文章
相關標籤/搜索