Javascript 繼承實現深度解析

一、原型繼承

原型繼承是Javascript中最基本也是最核心的繼承方式,如何實現原型鏈的繼承呢?由於Javascript中所有的對象都擁有原型屬性(即__proto__屬性),該屬性指向父對象,實現對象之間的繼承可以通過指定某個對象(即子對象)的原型__proto__為其父對象。在實踐中我們可以使用如下方式來實現原型繼承:

  
    function Parent(name){
        this.name = name;
        this.sayHello = function(){
            alert('hello ' + this.name);
        }
    }
    Parent.prototype.colors = ['blue', 'black', 'yellow'];

    function Child(name){
        this.name = name;
    }
    // 指定Child的原型為Parent
    Child.prototype = new Parent();
    Child.prototype.constructor = Child;

    var child1 = new Child('child1');
    var child2 = new Child('child2');
    child1.sayHello(); // hello child1
    child2.sayHello(); // hello child2
    alert(child1.colors); // blue, black, yellow
    child1.colors.push('red');
    alert(child2.colors); // blue, black, yellow, red
  

在上述代碼中,我們聲明了一個Parent函數和一個Child函數,Parent 函數中有 name 屬性和 sayHello 方法,Child 函數中只有 name 屬性,我們實現了 Child 的原型繼承自 Parent,並把 Child的原型的構造器設置為 Child自己,實例化 Child 後就可以訪問到 Parent的屬性和方法。

實踐中請注意:

  • 實現原型繼承時,需要在既定的基礎上對其進行開發,建議使用封裝性較好的函數進行開發,方便以後在其他項目也可以復用。
  • 你也可以通過Object.create函數進行原型繼承,這個函數可以上述代碼整合成如下:
  
    function object(p) {
        function F() {}
        F.prototype = p;
        return new F();
    }

    function Parent(name){
        this.name = name;
        this.sayHello = function(){
            alert('hello ' + this.name);
        }
    }
    Parent.prototype.colors = ['blue', 'black', 'yellow'];

    function Child(name){
        this.name = name;
    }
    // 指定Child的原型為Parent
    Child.prototype = Object.create(Parent.prototype);
    Child.prototype.constructor = Child;

    var child1 = new Child('child1');
    var child2 = new Child('child2');
    child1.sayHello(); // hello child1
    child2.sayHello(); // hello child2
    alert(child1.colors); // blue, black, yellow
    child1.colors.push('red');
    alert(child2.colors); // blue, black, yellow, red
  

二、經典繼承

經典繼承,即利用 call 或者 apply 方法來實現繼承。經典繼承相對於原型繼承來說,顯得更加靈活、高效。在設計模式中,很多模式基於經典繼承的思想來構建,這些設計模式中都在使用 call 和 apply 方法來實現調用父類構造函數。在實踐中我們可以使用如下方式來實現經典繼承:

  
    function Parent(name){
        this.name = name;
        this.sayHello = function(){
            alert('hello ' + this.name);
        }
    }
    Parent.prototype.colors = ['blue', 'black', 'yellow'];

    function Child(name){
        // 將 Parent 的作用域賦給 Child,以實現父類實例化
        Parent.call(this, name);
        this.age = 18;
    }

    var child = new Child('child');
    alert(child.name); // child
    alert(child.age); // 18
    alert(child.colors); // undefined
  

我們聲明了兩個函數,Parent 函數中有 name 屬性和 sayHello 方法,Child 函數中通過使用 Parent.call() 將 Parent 函數的作用域賦給了 Child,從而實現了 Child 對象繼承 Parent 對象的屬性和方法。這種方式不會污染父類的原型屬性,同時也可以在子類實例化時傳入屬性,實際開發中應用廣泛。

實踐中請注意:

  • 經典繼承可以實現單繼承或者多重繼承,如下例中既同時繼承自Father和Mother:
  
    function Father() {
          this.name = 'father';
      }

    function Mother() {
          this.age = 30;
      }

    function Child() {
          Father.call(this);
          Mother.call(this);
      }

    var child = new Child();
    console.log(child.name); // father
    console.log(child.age); // 30 
  

三、組合繼承

組合繼承,即將原型繼承和經典繼承進行組合來實現繼承。通過使用原型鏈實現對超類屬性(即方法)的共享,在通過調用父類構造函數來實現 prototype 中屬性的定義。在實踐中我們可以使用如下方式來實現組合繼承:

  
    function Parent(name){
        this.name = name;
        this.sayHello = function(){
            alert('hello ' + this.name);
        }
    }
    Parent.prototype.colors = ['blue', 'black', 'yellow'];

    function Child(name){
        Parent.call(this, name);
    }
    Child.prototype = new Parent();
    Child.prototype.constructor = Child;

    var child1 = new Child('child1');
    var child2 = new Child('child2');
    child1.sayHello(); // hello child1
    child2.sayHello(); // hello child2
    alert(child1.colors); // blue, black, yellow 
    child1.colors.push('red');
    alert(child2.colors); // blue, black, yellow
  

我們聲明了兩個函數,Parent 函數中有 name 屬性和 sayHello 方法,Child 函數中在調用 Parent.call(this, name) 的情況下實現經典繼承,然後在原型鏈上繼承了 Parent 的屬性和方法,最後將 Child 的構造函數重新賦值給 Child 的原型構造函數,從而實現 Child 對象的屬性和方法的繼承。關於組合繼承,在實踐中是一種非常好的方式。

實踐中請注意:

  • 在編寫組合繼承時,需要注意不要重複實例化父對象,我們可以使用 Object.create(Parent.prototype) 的方式來創建一個中介者。
  
    function Parent(name){
        this.name = name;
        this.sayHello = function(){
            alert('hello ' + this.name);
        }
    }
    Parent.prototype.colors = ['blue', 'black', 'yellow'];

    function Child(name){
        Parent.call(this, name);
    }
    Child.prototype = Object.create(Parent.prototype);
    Child.prototype.constructor = Child;

    var child1 = new Child('child1');
    var child2 = new Child('child2');
    child1.sayHello(); // hello child1
    child2.sayHello(); // hello child2
    alert(child1.colors); // blue, black, yellow 
    child1.colors.push('red');
    alert(child2.colors); // blue, black, yellow
  

四、寄生組合繼承

寄生組合繼承,即相對於組合繼承來說不會重複實例化父對象,而是創建一個空的函數作為中介者來實現繼承。在實踐中我們可以使用如下方式來實現寄生組合繼承:

  
    function Parent(name){
        this.name = name;
        this.sayHello = function(){
            alert('hello ' + this.name);
        }
    }
    Parent.prototype.colors = ['blue', 'black', 'yellow'];

    function Child(name){
        Parent.call(this, name);
    }
    var __ = function() {};
    __.prototype = Parent.prototype;
    Child.prototype = new __();
    Child.prototype.constructor = Child;

    var child1 = new Child('child1');
    var child2 = new Child('child2');
    child1.sayHello(); // hello child1
    child2.sayHello(); // hello child2
    alert(child1.colors); // blue, black, yellow 
    child1.colors.push('red');
    alert(child2.colors); // blue, black, yellow
  

在上述代碼中,我們通過創建名( __ )叫佔位符(thunk)的函數作為中介者來實現繼承,並將 Child 的原型指向這個中介器,避免了在使用 Object.create() 方法的重複實例化問題。實踐證明在大多數情況下,寄生組合繼承是一種更好的繼承方式,我們還可以通過Module模式對寄生組合繼承進行封裝,易於復用。

實踐中請注意:

  • 當某個父類中有多個子類時,我們可以將父類中公共的方法提取出來,在Child構造函數的裡面進行調用,達到代碼復用的目的。
  
    function Parent(name){
        this.name = name;
        this.sayHello = function(){
            alert('hello ' + this.name);
        }
    }
    Parent.prototype.colors = ['blue', 'black', 'yellow'];

    function Child(name){
        Parent.call(this, name);
    }
    function extend(child, parent){
        var Middle = function(){};
        Middle.prototype = parent.prototype;
        child.prototype = new Middle();
        child.prototype.constructor = child;
        child.prototype.parent = parent.prototype;
        return child;
    }

    extend(Child, Parent);

    var child1 = new Child('child1');
    var child2 = new Child('child2');
    child1.sayHello(); // hello child1
    child2.sayHello(); // hello child2
    alert(child1.colors); // blue, black, yellow 
    child1.colors.push('red');
    alert(child2.colors); // blue, black, yellow
  

五、ES6 繼承

ES6 提出了 extends 關鍵字,可以極大的簡化繼承的操作,我們只需要在子類中添加 extends 關鍵字,並在解析 super 關鍵字時使用 new 關鍵字調用父類的構造函數即可。在實踐中我們可以使用如下方式來實現ES6繼承:

  
     class Parent{
        constructor(name){
            this.name = name;
        }
        sayHello(){
            alert('hello ' + this.name);
        }
     }
     Parent.prototype.colors = ['blue', 'black', 'yellow'];

     class Child extends Parent{
        constructor(name){
            super(name)
        }
     }

     let child1 = new Child('child1');
     let child2 = new Child('child2');
     child1.sayHello(); // hello child1
     child2.sayHello(); // hello child2
     alert(child1.colors); // blue, black, yellow 
     child1.colors.push('red');
     alert(child2.colors); // blue, black, yellow
  

在上述代碼中,我們聲明了一個 Parent 類和一個 Child 類,Child 類直接繼承自 Parent 類,我們只需要在 Child 類中添加 extends 關鍵字,並在解析 super 關鍵字時使用 new 關鍵字調用父類的構造函數即可,如果是需要繼承父對象的屬性和方法,則直接使用 super 關鍵字即可。ES6 繼承更加直觀舒適,使用起來也非常方便。

實踐中請注意:

  • ES6繼承在編碼時需要注意瀏覽器的兼容性問題,如果是在較低版本的瀏覽器中使用的話,需要通過 babel 來實現ES6的編譯。
  • 可以在ES6中通過 Symbol.hasInstance 重

    原創文章,作者:QJQOE,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/369384.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
QJQOE的頭像QJQOE
上一篇 2025-04-12 13:01
下一篇 2025-04-12 13:01

相關推薦

  • 深度查詢宴會的文化起源

    深度查詢宴會,是指通過對一種文化或主題的深度挖掘和探究,為參與者提供一次全方位的、深度體驗式的文化品嘗和交流活動。本文將從多個方面探討深度查詢宴會的文化起源。 一、宴會文化的起源 …

    編程 2025-04-29
  • Python下載深度解析

    Python作為一種強大的編程語言,在各種應用場景中都得到了廣泛的應用。Python的安裝和下載是使用Python的第一步,對這個過程的深入了解和掌握能夠為使用Python提供更加…

    編程 2025-04-28
  • 使用JavaScript日期函數掌握時間

    在本文中,我們將深入探討JavaScript日期函數,並且從多個視角介紹其應用方法和重要性。 一、日期的基本表示與獲取 在JavaScript中,使用Date對象來表示日期和時間,…

    編程 2025-04-28
  • JavaScript中使用new Date轉換為YYYYMMDD格式

    在JavaScript中,我們通常會使用Date對象來表示日期和時間。當我們需要在網站上顯示日期時,很多情況下需要將Date對象轉換成YYYYMMDD格式的字元串。下面我們來詳細了…

    編程 2025-04-27
  • Python遞歸深度用法介紹

    Python中的遞歸函數是一個函數調用自身的過程。在進行遞歸調用時,程序需要為每個函數調用開闢一定的內存空間,這就是遞歸深度的概念。本文將從多個方面對Python遞歸深度進行詳細闡…

    編程 2025-04-27
  • Spring Boot本地類和Jar包類載入順序深度剖析

    本文將從多個方面對Spring Boot本地類和Jar包類載入順序做詳細的闡述,並給出相應的代碼示例。 一、類載入機制概述 在介紹Spring Boot本地類和Jar包類載入順序之…

    編程 2025-04-27
  • 深度解析Unity InjectFix

    Unity InjectFix是一個非常強大的工具,可以用於在Unity中修復各種類型的程序中的問題。 一、安裝和使用Unity InjectFix 您可以通過Unity Asse…

    編程 2025-04-27
  • JavaScript中修改style屬性的方法和技巧

    一、基本概念和方法 style屬性是JavaScript中一個非常重要的屬性,它可以用來控制HTML元素的樣式,包括顏色、大小、字體等等。這裡介紹一些常用的方法: 1、通過Java…

    編程 2025-04-25
  • CloneDeep函數在Javascript開發中的應用

    一、CloneDeep的概念 CloneDeep函數在Javascript中是一種深層克隆對象的方法,可以在拷貝對象時避免出現引用關係。使用者可以在函數中設置可選參數使其滿足多種拷…

    編程 2025-04-25
  • JavaScript中的Object.getOwnPropertyDescriptors()

    一、簡介 Object.getOwnPropertyDescriptors()是JavaScript中一個非常有用的工具。簡單來說,這個方法可以獲取一個對象上所有自有屬性的屬性描述…

    編程 2025-04-25

發表回復

登錄後才能評論