JavaScript 實現繼承的方式:原型鏈、組合式、寄生組合式

原型鏈繼承

let Parent = function() {
    this.name = [‘parent']
}

Parent.prototype.sayName = function() {
    console.log(this.name)
}

let Child = function(){}

Child.prototype = new Parent() //原型式繼承

let child1 = new Child()
child1.sayName() //[‘parent’]

child1.name.push(‘child’) 
let child2 = new Child()
child2.sayName() //[‘parent’, ‘child']

問題:

  • 所以實例共享引用類型的屬性,一改都改;
  • 且創建 child 實例無法向 Parent 傳參

組合式繼承

這是 JavaScript 中最常用的繼承模式。

let Parent = function(name){
    this.name = name
    this.staff = [‘a']
}

Parent.prototype.sayName = function(){
    console.log(this.name)
}
Parent.prototype.showStaff = function(){
    console.log(this.staff)
}

let Child = function(name){
    Parent.call(this, name) //借用 Parent 構造函數,實現向其傳參 (第二次)
}

Child.prototype = new Parent() // 繼承 Parent 原型上的方法 (第一次)

let child1 = new Child(‘child1’)
let child2 = new Child(‘child2’)

child1.sayName() //child1
child2.sayName() //child2

child1.staff.push(‘child1’)
child1.showStaff() //[‘a’, ‘child1’]
child2.showStaff() //[‘a’]

問題:

兩次調用 Parent 構造函數,分別讓 Child.prototypechild 實例兩個對象都有了 Parent 中的 namestaff 屬性。

寄生組合繼承

關鍵:

對於第一次調用,讓 Child.prototype 有了 Parent 的屬性,這是要避免的。

方法:

  • 創建一個空的函數中轉一下;
  • Child 的原型 Child.prototype = 空函數的實例;
  • 讓空函數的原型 F.prototype = Parent.prototype

代碼描述

let F = function() {};
F.prototype = Parent.prototype;
Child.prototype = new F();

由此,child 通過 __proto__ 指向Child.prototypef ,又通過 __proto__ 指向 F.prototypeParent.prototype

事實上,上面的思路就是 Object.create() 的實現

用代碼描述:

function create (obj) {
    let F = function(){}
    F.prototype = obj
    return new F()
}

由於 Child.prototype = new F() ,需要重新做 constructor 等指向,最後得

function inheritPrototype (subType, superType) {
    let prototype = Object.create(superType.prototype)
    subType.prototype = prototype
    prototype.constructor = subType

寄生組合繼承代碼

let Parent = function(name){
    this.name = name
    this.staff = [1]
}

Parent.prototype.sayName = function(){
    console.log(this.name)
}

Parent.prototype.showStaff = function(){
    console.log(this.staff)
}

let Child = function(name){
    Parent.call(this, name)
}

//Child.prototype = new Parent()

function inheritPrototype (subType, superType) {
    let prototype = Object.create(superType.prototype)
    subType.prototype = prototype
    prototype.constructor = subType
}

inheritPrototype(Child, Parent)

let child1 = new Child(‘child1’)
child1.sayName()
let child2 = new Child(‘child2’)
child2.sayName()
child1.staff.push(2)
child1.showStaff()
child2.showStaff()

好處

《JavaScript高級程序設計》表示:

這種方式的高效率體現它只調用了一次 Parent 構造函數,並且因此避免了在 Parent.prototype
上面創建不必要的、多餘的屬性。與此同時,原型鏈還能保持不變;因此,還能夠正常使用 instanceof 和
isPrototypeOf。開發人員普遍認爲寄生組合式繼承是引用類型最理想的繼承範式。

參考:https://github.com/mqyqingfeng/Blog/issues/16

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章