1. 程式人生 > >JavaScript常用的繼承方式

JavaScript常用的繼承方式

JavaScript常用繼承方式主要分為(7種):原型鏈繼承、建構函式繼承、組合繼承、原型式繼承、寄生式繼承、寄生組合繼承以及繼承多個物件。

1:原型鏈繼承(核心:將父類的例項作為子類的原型)

基本概念:重寫原型物件,賦予一個新的物件的例項。基本思想就是讓一個原型物件指向另一個父類的例項。

    function Super() {
        //基本資料型別
        this.text = 'Hello';
    }
    Super.prototype.getSuperText = function() {
        return this.text;
    }
    function Sub() {
    	this.subText = 'Word';
    }
    
    Sub.prototype = new Super();
    
    const instance = new Sub();
    console.log(instance);

特點:非常純粹的繼承關係,例項是子類的例項,也是父類的例項。父類新增原型方法或屬性,子類都能訪問到。

優點:簡單易於操作

缺點:對引用型別資料操作會互相(多個例項之間)影響

    function Super() {
        //複雜物件,也就是引用型別
        this.value = [1, 2, 3, 4];
    }
    Super.prototype.getSuperValue = function() {
        return this.value;
    }
    function Sub() {
        this.subText = 'Word';
    }
    
    Sub.prototype = new Super();
    
    const instance1 = new Sub();
    const instance2 = new Sub();
    
    instance1.value.push(5);
    console.log(instance2.value);
    
    // (5) [1, 2, 3, 4, 5]

2:建構函式繼承

    //定義建構函式
    function Super(){
        this.value = [1, 2, 3, 4];
    }
    //新增屬性getSuperValue
    Super.prototype.getSuperValue = function() {
        return this.value;
    }
    //sub每次執行都要重新呼叫
    function Sub(){
        Super.call(this);
    }
    
    const instance1 = new Sub();
    instance1.value.push(5);
    console.log(instance1.value);
    // (5) [1, 2, 3, 4, 5]
    
    const instance2 = new Sub();
    console.log(instance2.value);
    // (4) [1, 2, 3, 4]

建構函式的特點:(對引用資料型別沒有影響)上面的程式碼輸出instance1是1,2,3,4,5,instance2是1,2,3,4。這是因為sub每次在執行時都是重新呼叫了一個super.call(),而且建構函式在構建物件的過程中,每次都是建立了一個新的object,因此每次呼叫sub都會執行一遍super,每次執行時都會有申請一個新的記憶體空間,所以得到的兩個value值是不一樣互不影響的。

缺點:在整個建構函式的基礎過程中,上面的程式碼並沒有使用proto和prototype的屬性,沒有使用的話那麼原型鏈就沒有接上。所以說,建構函式基礎只能繼承父類的例項屬性和方法,不能繼承原型鏈上的屬性和方法

3:組合繼承

通過呼叫父類構造,繼承父類的屬性並保留傳參的優點,然後通過將父類例項作為子類原型,實現函式複用。

保留了建構函式繼承與原型鏈繼承的優點。但是執行了兩次Person,屬性重複了。

    function Person(name) {
        this.name = name;
        this.value = ["head", "body", "legs"];
    }
    Person.prototype.getName = function() {
        return this.name;
    };
    
    // 建構函式繼承
    function Teacher(name, school){
        // 執行又一次Person
        Person.call(this, name);
        this.school = school;
    }
    
    // 原型鏈繼承
    // 執行一次Person
    Teacher.prototype = new Person(); 
    const Eric = new Teacher("Eric",27);
    Eric.getName();
    // 輸出:Eric
    
    
    
    // prototype構造器指回自己 
    Teacher.prototype.constructor = Teacher;
    
    Teacher.prototype.getSchool = function() {
        return this.school;
    };

特點:既可以繼承例項屬性和方法,也可以繼承原型屬性和方法。既是子類的例項也是父類的例項,不存在引用屬性共享的問題。可以傳參,函式可複用。

缺點:呼叫兩次父類建構函式,生成了兩份例項。

4:原型式繼承

藉助原型可以基於已有的物件建立新的物件,同時還不必因此建立自定義型別。

原理:(本質)利用一個空物件作為一箇中介。

    const lakers = {
        name: "lakers",
        value: ["Micheal", "Wade", "Kobe"]
    };
    
    const lakers1 = Object.create(lakers);
    const lakers2 = Object.create(lakers);
    
    lakers1.value.push('Fish');
    console.log(lakers);

模擬Object.create()

object.create()原理:用一個函式包裝一個物件,然後返回這個函式的呼叫,這個函式就變成了一個可以隨意添增屬性的例項或物件。

    Object.prototype.create = function(obj) {
        function Fun() {}
        Fun.prototype = obj;
        return new Fun();
    }

缺點有兩點:第一點是無法傳遞引數,第二點是引用型別存在變數的汙染。****

5:寄生式繼承

寄生式繼承的思路與寄生建構函式和工廠模式類似,即建立一個僅用於封裝繼承過程的函式。

目的:在原型式繼承的基礎上,寄生增加了一些新的方法和屬性。

它的特點同原型式繼承一樣,也是無法傳遞引數,而且引用的資料型別也容易存在樣式汙染。

Object.createNew()

    Object.prototype.createNew = function(obj){
        var newObj = Object.create(obj);
        //獲取長度等於一個function
        newObj.getLength = function(){ ... };
        return newObj;
    }

6:寄生組合繼承

目的:為了解決資料重複拷貝兩遍的問題。

Super只執行一次。

    //定義Super建構函式
    function Super(name) {
      this.name = name;
      this.value = ["Hello", "Word"];
    }
    //在super的原型鏈新增一個getName
    Super.prototype.getName = function() {
      return this.name;
    };
    //定義Sub
    function Sub(name, age) {
      //呼叫建構函式繼承
      Super.call(this, name);
      this.age = age;
    }
    
    let prototype = Object.create(Super.prototype);
    prototype.constructor = Sub;
    Sub.prototype = prototype;
    
    Sub.prototype.getAge = function(){
      return this.age;
    }
    
    const instance1 = new Sub("Eric", 23);
    const instance2 = new Sub("Vico", 23);
    instance1.value.push("!");
    instance2.value.push("!!");

7:繼承多個物件

藉助原型式繼承Object.create拿到SuperClass,也就是父類,拿到父類的prototype之後把它賦給ClassOne,

再然後我們將ClassTwo的prototype使用一個Object.assign,一個物件的拷貝,把它拷貝到ClassOne裡面來,

然後最後ClassOne.prototype.constructor等於ClassOne

也就是使用一個Class.assign把所有我們想要繼承的父類的prototype全部組合到一起完成一個拷貝,之後再賦給物件。

    function 
    
    ClassOne.prototype = Object.create(SuperClass.prototype);
    
    Object.assign(ClassOne.prototype, ClassTwo.prototype);
    
    ClassOne.prototype.constructor