JavaScript中的繼承(上)


 在ES6之前,JavaScript中的繼承是通過原型鏈來實現的;下面對此進行分析:

一、原型鏈

  首先需要明白構造函數、原型、實例之間的關係:
    1、每一個構造函數都有一個指向原型對象的prototype指針;
    2、原型對象有一個指向構造函數的constructor指針;
    3、每個實例都有一個指向原型對象的[[Prototype]]指針;
    
    原型鏈方法實現繼承的基本思想就是:讓原型對象等於另一個類型的實例,那麼此時的原型對象包含着指向另一個原型的指針,以此類推,實現繼承;

實現原型鏈的一種基本模式如下:
    function SuperType() {
        this.property = true;
    }
    SuperType.prototype.getSuperValue = function () {
        return this.property;
    };
    function SubType() {
        this.subproperty = false;
    }
    SubType.prototype = new SuperType();
    SubType.prototype.getSubValue = function () {
        return this.subproperty;
    };
    var instance = new SubType();
    console.log(instance.getSuperValue());//true
   以上代碼,實現了SubType對SuperType的繼承,這個例子中實例和原型的關係圖如下:

    這種模式實現的繼承也有其缺點:
    1、我們的原型會替換成另一個類型的實例,那麼之前的實例屬性變成了現在的原型屬性,爲最底層的所有實例共享;
    2、創建子類型的實例時,沒有辦法在不影響所有實例的情況下向超類型的構造函數傳遞參數;

    基於以上缺點,實際應用中很少單獨使用原型鏈,爲解決這一問題,開發人員又提出一種借用構造函數的技術

二、借用構造函數

   借用構造函數技術(又叫僞造對象或經典繼承)的基本思想很簡單:在子類型的構造函數內部調用超類型的構造函數。舉例如下:
    function SuperType(name) {
        this.name = name;
        this.friends = ['Alice'];
    }
    function SubType() {
        SuperType.call(this, 'Jack');
        this.age = 29;
    }
    var instance1 = new SubType();
    var instance2 = new SubType();
    instance1.friends.push('Bob');
    console.log(instance1.name + ' ' + instance1.age);//Jack 29
    console.log(instance2.name + ' ' + instance2.age);//Jack 29
    console.log(instance1.friends);//["Alice", "Bob"]
    console.log(instance2.friends);//["Alice"]

    由以上代碼可以看到:
    1、在創建SubType的實例時,在其構造函數內會調用SuperType的構造函數,結果是每個實例都保存着SuperType中屬性的一個副本(通過操作friends屬性可以得到驗證);
    2、和原型鏈方式相比,借用構造函數的最大的優勢是可以向父類傳遞參數(代碼中的name參數);

   當然借用構造函數也有問題:函數都在構造函數中定義,那麼每個實例都會有一個副本,無法實現函數複用。而且父類原型中的方法對子類不可見;
   
    考慮到構造函數技術的缺點,單獨使用也很少見,更多優秀的繼承方法請參考:JavaScript繼承中篇
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章