5種實現繼承方式


一、原型鏈繼承 (很少用)

原理:讓子類構造函數的原型指向父類型構造函數的一個實例

// 定義Animal構造函數
    function Animal(color) {
        this.color = color;
        this.eat = ['肉']; // 引用類型屬性
    }
    Animal.prototype.eating = function () {
        console.log("我能吃"+this.eat);
    };
    // 定義貓咪構造函數
    function Cat(name) {
        this.name = name;
    }
    // Cat原型指向Animal的一個實例!!!
    /**************重點語句**********/
    Cat.prototype = new Animal('yellow');
    /********************************/
    // 實例化Cat
    var c1 = new Cat('咪咪');
    var c2 = new Cat('泡泡');
    c1.color = 'black';
    c2.color = 'white'
    c1.eat.push('貓薄荷');
    c1.eating(); //我能吃肉,貓薄荷
    console.log(c1.color); //black
    console.log(c2.color); //white
    console.log(c1.constructor); // Animal函數體,不再是Cat了
    // 存在問題
    console.log(c1.eat); // 肉,貓薄荷
    console.log(c2.eat); // 肉,貓薄荷  受到實例c1的影響

存在問題:父構造函數中的屬性都會成為共享屬性,當父構造函數中有引用類型屬性時,實例之間會相互影響

二、借用構造函數(偽造對象)(很少用)

原理:在子類型構造函數中執行父類構造函數,並將父類構造函數的this指向子類的new出來的對象上

// 定義Animal構造函數
    function Animal(color) {
        this.color = color;
        this.eat = ['肉']; // 引用類型屬性
    }
    Animal.prototype.eating = function () {
        console.log(this.eat);
    }
    // 定義貓咪構造函數
    function Cat(name) {
        this.name = name;
        // 繼承Animal 將Animal綁定到this對象上並執行!!!
        /**************重點語句**********/
        Animal.call(this,'red');
        /*******************************/
    }

    // 實例化Cat
    var c1 = new Cat('咪咪');
    var c2 = new Cat('泡泡');
    c1.color = 'black';
    c2.color = 'white'
    c1.eat.push('貓薄荷');
    c1.eating();  // 報錯 因為父類原型的方法並沒有繼承到
    c2.eating();  // 報錯 因為父類原型的方法並沒有繼承到
    console.log(c1.color); //black
    console.log(c2.color); //white
    console.log(c1.constructor); // Cat函數體
    console.log(c1.eat); // 肉,貓薄荷
    console.log(c2.eat); // 肉   不受實例c1的影響

解決問題:父類有引用類型對象的屬性時,繼承后實例間也不會相互影響

存在問題:1、父類中相同的方法也需要創建多次,浪費內存 2、子類訪問不到父類原型的屬性和方法了。

三、組合繼承(常用的繼承方式)

(組合原型鏈繼承和借用構造兩種方式)

// 定義Animal構造函數
    function Animal(color) {
        this.color = color;
        this.eat = ['肉']; // 引用類型屬性
    }
    Animal.prototype.eating = function () {
        console.log("我能吃"+this.eat);
    };
    // 定義貓咪構造函數
    function Cat(name) {
        this.name = name;
        // 繼承Animal 將Animal綁定到this對象上並執行!!!
        /**************重點語句1**********/
        Animal.call(this,'red');
        /*******************************/
    }
    // Cat原型指向Animal的一個實例!!!
    /**************重點語句2**********/
    Cat.prototype = new Animal('yellow');
    /*******************************/
    // 實例化Cat
    var c1 = new Cat('咪咪');
    var c2 = new Cat('泡泡');
    c1.color = 'black';
    c2.color = 'white'
    c1.eat.push('貓薄荷');
    c1.eating(); //我能吃肉,貓薄荷
    console.log(c1.color); //black
    console.log(c2.color); //white
    console.log(c1.constructor); // Animal函數體,不再是Cat了
    // 存在問題
    console.log(c1.eat); // 肉,貓薄荷
    console.log(c2.eat); // 肉  不受實例c1的影響

這種方式解決了原型鏈繼承和借用構造函數繼承兩種方式帶來的問題,融合了兩種方式的優點,目前最常用的方式

存在一個小問題,此時子類的constructor不再是自身構造函數了

四、經典繼承(規范化的原生式繼承)

原理:利用Object.create()

// 定義一個字面量對象
    var animal = {
        name: '動物',
        color: 'red',
        eat: ['肉'],
        eating: function () {
            console.log('我能吃'+this.eat);
        }
    };
    /*************重點語句**********/
    var cat = Object.create(animal, {
        name: {
            value: "貓咪"
        }  // 重寫屬性會覆蓋繼承來的name屬性,修改的是自身的name,不是animal的
    });
    cat.name = '小貓咪';  //這樣修改不了自身的name,還是貓咪

    cat.eat.push('貓薄荷');
    cat.color = 'yellow';
    var dog = Object.create(animal); // 也可以省略第二個參數
    console.log(cat.name);  // 貓咪
    console.log(dog.name);  // 動物
    console.log(cat.eat);   // ‘肉’ ‘貓薄荷’
    console.log(dog.eat);   // ‘肉’ ‘貓薄荷’
    console.log(cat.color);   // yellow
    console.log(dog.color);   // red
    cat.eating();  // 我能吃‘肉’ ‘貓薄荷’
    dog.eating();  // 我能吃‘肉’ ‘貓薄荷’

優點:不需要興師動眾使用構造函數

存在問題:對象中有引用類型值屬性時,實例間會相互影響

五、寄生組合式繼承

在組合繼承的前提下,子類原型指向父類原型的一個副本而不是父類的實例

 // 定義Animal構造函數
    function Animal(color) {
        this.color = color;
        this.eat = ['肉']; // 引用類型屬性
    }
    Animal.prototype.eating = function () {
        console.log("我能吃"+this.eat);
    };
    // 定義貓咪構造函數
    function Cat(name) {
        this.name = name;
        // 繼承Animal 將Animal綁定到this對象上並執行!!!
        /**************重點語句1**********/
        Animal.call(this,'red');
        /*******************************/
    }
    /**********重點語句2*********/

    function inheritPrototype(son, father) {
        var prototype = Object(father.prototype); // 將父類的原型創建一個副本
        prototype.constructor = son; //給副本指定constructor為子類構造函數
        son.prototype = prototype; //將子類原型指向這個副本
    }

    inheritPrototype(Cat,Animal);
    /****************************/
    // 實例化Cat
    var c1 = new Cat('咪咪');
    var c2 = new Cat('泡泡');
    c1.color = 'black';
    c2.color = 'white'
    c1.eat.push('貓薄荷');
    c1.eating(); //我能吃肉,貓薄荷
    console.log(c1.color); //black
    console.log(c2.color); //white
    console.log(c1.constructor); // cat函數體,
    console.log(c1.eat); // 肉,貓薄荷
    console.log(c2.eat); // 肉  不受實例c1的影響

優化了組合繼承(子類.construtor指向問題)的小問題,是最理想的繼承了


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM