你知道JavaScript的繼承有幾種寫法嗎?

風靈使發表於2018-08-17

標題的靈感來源於魯迅的小說《孔乙己》中孔乙己和小夥計的一段對話:“茴香豆的茴字,怎樣寫的?……回字有四樣寫法,你知道麼?”

這裡我們並不探討封建制度下窮苦潦倒的讀書人的迂腐,回字的幾種寫法留給漢語言的同學去研究吧,今天我們討論JavaScript繼承的幾種寫法,由淺入深,一層層剝開她的面紗,最後給出一個最佳實踐。

一、通過建構函式實現繼承

    function Parent() {
      this.name = 'name';
    }
    Parent.prototype.say = function () {
      console.log('say');
    }
    function Child() {
      Parent.call(this);
      this.type = 'child';
    }
    const child = new Child();

這種方式通過在子類的建構函式中呼叫父建構函式實現繼承,但是有個致命的缺點:如下圖,子類例項並不能繼承父類中原型物件上的屬性或者方法。

這裡寫圖片描述

二、通過原型物件實現繼承

    function Parent() {
      this.name = 'name';
      this.favor = ['blue', 'red'];
    }
    Parent.prototype.say = function () {
      console.log('say');
    }
    function Child() {
      this.type = 'child';
    }
    Child.prototype = new Parent();
    const child = new Child();

這種方式通過給子類建構函式的prototype物件賦值實現繼承,如下圖,無論是name屬性還是say方法都能被子類例項訪問到。
這裡寫圖片描述
但是這種方式也有缺點,如下圖,當父類的屬性是引用型別時,子類例項繼承的是同一份屬性,任一例項改變該屬性都會引起全域性變化,無法互相隔離。
這裡寫圖片描述

三、組合方式

    function Parent() {
      this.name = 'name';
      this.favor = ['blue', 'red'];
    }
    Parent.prototype.say = function () {
      console.log('say');
    }
    function Child() {
      Parent.call(this);
      this.type = 'child';
    }
    Child.prototype = new Parent();
    const child = new Child();

結合了上兩種方式,避免了上面的缺點。但是這種方式在每次例項化子類例項時,都會呼叫兩次父類建構函式,需要優化。

四、組合優化①

    function Parent() {
      this.name = 'name';
      this.favor = ['blue', 'red'];
    }
    Parent.prototype.say = function () {
      console.log('say');
    }
    function Child() {
      Parent.call(this);
      this.type = 'child';
    }
    Child.prototype = Parent.prototype;
    const child = new Child();

在給子類prototype賦值時不要採用例項化父類的方式,直接賦值父類的prototype

其實,這種方式也有缺點:如下圖,子類例項的constructor屬性直接指向了父類建構函式,導致無法判斷當前物件例項化自哪個建構函式。

這裡寫圖片描述

五、組合優化②

    function Parent() {
      this.name = 'name';
      this.favor = ['blue', 'red'];
    }
    Parent.prototype.say = function () {
      console.log('say');
    }
    function Child() {
      Parent.call(this);
      this.type = 'child';
    }
    Child.prototype = Object.create(Parent.prototype);
    Child.prototype.constructor = Child;
    const child = new Child();

終極改造完成:通過Object.create()方法指定了子類例項的__proto__屬性,同時顯式宣告子類的constructor

相關文章