前端必知必會ES5、ES6的7種繼承

arzh發表於2019-07-15

眾所周知,在ES6之前,前端是不存在類的語法糖,所以不能像其他語言一樣用extends關鍵字就搞定繼承關係,需要一些額外的方法來實現繼承。下面就介紹一些常用的方法,紅寶書已經概括的十分全面了,所以本文基本就是對紅寶書繼承篇章的筆記和梳理。

原型鏈繼承

function Parent() {
    this.name = 'arzh'
}

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

function Child() {
    
}

//主要精髓所在
Child.prototype = new Parent()
Child.prototype.constructor = Child

var arzhChild = new Child()

arzhChild.getName() // 'arzh'
複製程式碼

原型鏈繼承缺點:

  1. 每個例項對引用型別屬性的修改都會被其他的例項共享
function Parent() {
    this.names = ['arzh','arzh1'];
}

function Child() {
    
}

//主要精髓所在
Child.prototype = new Parent()
Child.prototype.constructor = Child

var arzhChild2 = new Child()
arzhChild2.names.push('arzh2')
console.log(arzhChild2.names) //[ 'arzh', 'arzh1', 'arzh2' ]

var arzhChild3 = new Child()
arzhChild3.names.push('arzh3')
console.log(arzhChild3.names) //[ 'arzh', 'arzh1', 'arzh2', 'arzh3' ]
複製程式碼
  1. 在建立Child例項的時候,無法向Parent傳參。這樣就會使Child例項沒法自定義自己的屬性(名字)

借用建構函式(經典繼承)

function Parent() {
    this.names = ['arzh','arzh1']
}

function Child() {
    Parent.call(this)
}

var arzhChild2 = new Child()
arzhChild2.names.push('arzh2')
console.log(arzhChild2.names) //[ 'arzh', 'arzh1', 'arzh2' ]

var arzhChild3 = new Child()
arzhChild3.names.push('arzh3')
console.log(arzhChild3.names) //[ 'arzh', 'arzh1', 'arzh3' ]
複製程式碼

優點:

  1. 解決了每個例項對引用型別屬性的修改都會被其他的例項共享的問題
  2. 子類可以向父類傳參
function Parent(name) {
    this.name = name
}

function Child(name) {
    Parent.call(this, name)
}
    
var arzhChild = new Child('arzh');

console.log(arzhChild.name); // arzh

var arzhChild1 = new Child('arzh1');

console.log(arzhChild1.name); // arzh1

複製程式碼

缺點:

  1. 無法複用父類的公共函式
  2. 每次子類構造例項都得執行一次父類函式

組合式繼承(原型鏈繼承和借用建構函式合併)

function Parent(name) {
    this.name = name
    this.body = ['foot','hand']
}

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

Child.prototype = new Parent()
Child.prototype.constructor = Child

var arzhChild1 = new Child('arzh1', '18')
arzhChild1.body.push('head1')
console.log(arzhChild1.name,arzhChild1.age) //arzh1 18
console.log(arzhChild1.body) //[ 'foot', 'hand', 'head1' ]

var arzhChild2 = new Child('arzh2', '20')
arzhChild2.body.push('head2')
console.log(arzhChild2.name,arzhChild2.age) //arzh2 20
console.log(arzhChild2.body) //[ 'foot', 'hand', 'head2' ]
複製程式碼

優點:

  1. 解決了每個例項對引用型別屬性的修改都會被其他的例項共享的問題
  2. 子類可以向父類傳參
  3. 可實現父類方法複用

缺點:

  1. 需執行兩次父類建構函式,第一次是Child.prototype = new Parent(),第二次是Parent.call(this, name)造成不必要的浪費

原型式繼承

複製傳入的物件到建立物件的原型上,從而實現繼承

function createObj(o) {
    function F(){}
    F.prototype = o;
    return new F();
}
var person = {
    name : 'arzh',
    body : ['foot','hand']
}
var person1 = createObj(person)
var person2 = createObj(person)

console.log(person1) //arzh
person1.body.push('head') 
console.log(person2) //[ 'foot', 'hand', 'head' ]
複製程式碼

缺點: 同原型鏈繼承一樣,每個例項對引用型別屬性的修改都會被其他的例項共享

寄生式繼承

我們可以使用Object.create來代替上述createObj的實現,原理基本上是一樣的。寄生式繼承其實就是在createObj的內部以某種形式來增強物件(這裡的增強可以理解為新增物件的方法),最後返回增強之後的物件。

function createEnhanceObj(o) {
    //代替原型式繼承的createObj
    var clone = Object.create(o)
    clone.getName = function () {
        console.log('arzh')
    }
    return clone;
}
複製程式碼

通過createEnhanceObj就可以在建立物件的時候,把物件方法也通過此種方式繼承。
缺點: 同借用建構函式一樣,無法複用父類函式,每次建立物件都會建立一遍方法

寄生組合式繼承

不需要為了子類的原型而多new了一次父類的建構函式,如Child.prototype = new Parent() 只需要複製父類原型的一個副本給子類原型即可

function inheritPrototype(Parent, Child){
	Child.prototype = Object.create(Parent.prototype) //建立父類原型的一個副本,把副本賦值給子類原型
	Child.prototype.constructor = Child;
}

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

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

function Child(color) {
    Parent.call(this, 'arzh')
    this.color = color
}

inheritPrototype(Parent, Child)

var arzhChild = new Child('red')
console.log(arzhChild.name) // 'arzh'

複製程式碼

優點: 不必為了指定子型別的原型而呼叫父型別的建構函式

ES6繼承

ES6支援通過類來實現繼承,方法比較簡單,程式碼如下

class Point {
    constructor(x, y) {
        this.x = x
        this.y = y
    }
    
    toString() {
        return this.x + '' + this.y
    }
}

class ColorPoint extends Point {
    constructor(x, y, color) {
        super(x, y) //呼叫父類的constructor(x, y)
        this.color = color
    }
    
    toString() {
        return this.color + ' ' + super.toString() // 呼叫父類的toString()
    }
}

var colorPoint = new ColorPoint('1', '2', 'red')

console.log(colorPoint.toString())  // red 12
複製程式碼

對本文有任何優化建議,可掃描下述二維碼一起討論,同時也希望大家多多關注,會不定期傳送一些原創文章

前端必知必會ES5、ES6的7種繼承

相關文章