前端JS實現繼承的多種方式

JS實現繼承的方式

// 定義一個父類
    function Father(name) {
      // 屬性
      this.name = name || "father";
      // 實例方法
      this.sayName = function() {
        console.log(this.name);
      };
      this.color = ["red", "blue"];
    }
    // 原型方法
    Father.prototype.age = 18;
    Father.prototype.sayAge = function() {
      console.log(this.age);
    };

1.原型鏈繼承:將父類的實例作爲子類的原型


function Son(name) {
      this.name = name || "son";
    }

    Son.prototype = new Father();

    let s1 = new Son("s1");
    let s2 = new Son("s2");

    s1.color.push("black");

    console.log(s1.name); //s1
    console.log(s1.color); //['red','blue','black']
    console.log(s1.age); //18
    s1.sayAge(); //18
    console.log(s2.name); //s2
    console.log(s2.color); //['red','blue','black']
優點:
1. 簡單,易於實現
2. 父類新增原型方法、原型屬性,子類都能訪問到
缺點:
1. 無法實現多繼承,因爲原型一次只能被一個實例更改
2. 來自原型對象的所有屬性被所有實例共享(上訴例子中的color屬性)
3. 創建子類實例時,無法向父構造函數傳參

2.構造繼承繼承:複製父類的實例屬性給子類


function Son(name) {
      Father.call(this, "我是傳給父類的參數");
      this.name = name || "son";
    }
    let s = new Son("son");
    console.log(s.name); // son
    //s.sayAge(); // 拋出錯誤(無法繼承父類原型方法)
    s.sayName(); // son
    console.log(s.age); // undefined (無法繼承父類原型屬性)
    console.log(s instanceof Father); // false
    console.log(s instanceof Son); // true
優點:
1. 解決了原型鏈繼承中子類實例共享父類引用屬性的問題
2. 創建子類實例時,可以向父類傳遞參數
3. 可以實現多繼承(call多個父類對象)
缺點:
1. 實例並不是父類的實例,只是子類的實例
2. 只能繼承父類實例的屬性和方法,不能繼承其原型上的屬性和方法3. 無法實現函數複用,每個子類都有父類實例函數的副本,影響性能

3.組合繼承:將原型鏈和借用構造函數的技術組合到一塊。使用原型鏈實現對原型屬性和方法的繼承,而通過構造函數來實現對實例屬性的繼承

 function Son(name) {
     // 第一次調用父類構造器 子類實例增加父類實例
      Father.call(this, "我是傳給父類的參數");
      this.name = name || "son";
    }
    // 經過new運算符 第二次調用父類構造器 子類原型也增加了父類實例
    Son.prototype = new Father();

    let s = new Son("son");
    console.log(s.name); // son
    s.sayAge(); // 18
    s.sayName(); // son
    console.log(s.age); // 18
    console.log(s instanceof Father); // true
    console.log(s instanceof Son); // true
    console.log(s.constructor === Father); // true
    console.log(s.constructor === Son); // false
優點:
1. 彌補了構造繼承的缺點,現在既可以繼承實例的屬性和方法,也可以繼承原型的屬性和方法
2. 既是子類的實例,也是父類的實例
3. 可以向父類傳遞參數
4. 函數可以複用
缺點:
1. 調用了兩次父類構造函數,生成了兩份實例
2. constructor指向問題

4.實例繼承:爲父類實例添加新特徵,作爲子類實例返回

 function Son(name) {
      let f=new Father('傳給父類的參數')
      f.name=name||'son'
      return f
    }

    let s = new Son("son"); //或者直接調用子類構造函數 let s = Son("son");
    console.log(s.name); // son
    s.sayAge(); // 18
    s.sayName(); // son
    console.log(s.age); // 18
    console.log(s instanceof Father); // true
    console.log(s instanceof Son); // false
    console.log(s.constructor === Father); // true
    console.log(s.constructor === Son); // false
優點:
1. 不限制調用方式,不管是new 子類()還是子類(),返回的對象具有相同的效果
缺點:
1. 實例是父類的實例,不是子類的實例2. 不支持多繼承

5.拷貝繼承:對父類實例中的的方法與屬性拷貝給子類的原型

function Son(name) {
      let f = new Father("傳給父類的參數");
      for (let k in f) {
        Son.prototype[k] = f[k];
      }
      Son.prototype.name = name;
    }

    let s = new Son("son");
    console.log(s.name); // son
    s.sayAge(); // 18
    s.sayName(); // son
    console.log(s.age); // 18
    console.log(s instanceof Father); // false
    console.log(s instanceof Son); // true
    console.log(s.constructor === Father); // false
    console.log(s.constructor === Son); // true
優點:
1. 支持多繼承
缺點:
1. 效率低,性能差,佔用內存高(因爲需要拷貝父類屬性)
2. 無法獲取父類不可枚舉的方法(不可枚舉的方法,不能使用for-in訪問到)

6.寄生組合繼承:通過寄生方式,砍掉父類的實例屬性,避免了組合繼承生成兩份實例的缺點

function Son(name) {
      Father.call(this);
      this.name = name || "son";
    }

    // 方法一  自己動手創建一箇中間類
    // (function() {
    //   let NoneFun = function() {};
    //   NoneFun.prototype = Father.prototype;
    //   Son.prototype = new NoneFun();
    //   Son.prototype.constructor = Son;
    // })();

    // 方法二  直接借用Object.create()方法
    Son.prototype = Object.create(Father.prototype);
    // 修復構造函數指向
    Son.prototype.constructor = Son;

    let s = new Son("son");
    console.log(s.name); // son
    s.sayAge(); // 18
    s.sayName(); // son
    console.log(s.age); // 18
    console.log(s instanceof Father); // true
    console.log(s instanceof Son); // true
    console.log(s.constructor === Father); // false
    console.log(s.constructor === Son); // true
優點:
1. 比較完美(js實現繼承首選方式)
缺點:
1.實現起來較爲複雜(可通過Object.create簡化)

7.es6–Class繼承:使用extends表明繼承自哪個父類,並且在子類構造函數中必須調用super

class Son extends Father {
      constructor(name) {
        super(name);
        this.name = name || "son";
      }
    }

    let s = new Son("son");
    console.log(s.name); // son
    s.sayAge(); // 18
    s.sayName(); // son
    console.log(s.age); // 18
    console.log(s instanceof Father); // true
    console.log(s instanceof Son); // true
    console.log(s.constructor === Father); // false
    console.log(s.constructor === Son); // true
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章