JavaScript各種繼承方式和優缺點


    好久沒寫博客啦,嘻嘻,這個月是2017年的最后一個月啦,大家應該都開始忙着寫年終總結了吧,嘻嘻,小穎今天給大家分享下Javascript中的幾種繼承方式以及他們的優缺點。

1.借助構造函數實現繼承

原理:通過call()函數修改 this 指向,從而實現將父類屬性掛載到子類實例中。

    function parent1() {
        this.name = 'parent1';
    }
    function child1() {
        parent1.call(this);
        this.type = 'child1';
    }
    console.log(new child1);

 打印結果:

當我們給父類 parent1 的 prototype 屬性添加say方法后,但是在 child1 中是獲取不到的。

    function parent1() {
        this.name = 'parent1';
    }
    parent1.prototype.say = function() {
        console.log('hello');
    };

    function child1() {
        parent1.call(this);
        this.type = 'child1';
    }
    console.log(new child1, new child1().say());

打印結果:

所以.借助構造函數實現繼承,只能實現部分繼承;如果父類屬性都在構造函數中,則能夠實現全部繼承,如果父類原型對象上還有方法,則子類是繼承不到的。

總結:

     優點:
           1.只調用一次父類的構造函數,避免了在子類原型上創建不必要的,多余的屬性 。
           2.原型鏈保持不變。
     缺點:只能實現部分繼承;如果父類屬性都在構造函數中,則能夠實現全部繼承,如果父類原型對象上還有方法,則子類是繼承不到的。

2.借助原型鏈實現繼承(最通用的方式)

原理:將子類的prototype屬性賦值為父類實例對象,則子類的_proto_屬性繼承父類。

    function parent2() {
        this.name = 'parent2';
        this.play = [1, 2, 3];
    }
    parent2.prototype.say = function() {
        console.log('hello');
    };

    function child2() {
        this.type = 'child2';
    }
    child2.prototype = new parent2();
    console.log(new child2);
    var p1 = new child2();
    var p2 = new child2();
    console.log(p1.say());
    console.log(p1.play, p2.play);
    p1.play.push(4);
    console.log(p1, p2);
    console.log(p1.play, p2.play);

打印結果:

注意:

1.在第一種繼承方式中,子類是繼承不到父類 prototype 屬性的內容的,但現在可以繼承到了。

2.其實小穎只執行了  p1.play.push(4)  ,然而 p2.play 的值也跟着變化了。

這其實都是因為   child2.prototype = new parent2(),他們的 __proto__ 都繼承了父類parent2 的所有屬性。雖然表面上  p1.play.push(4  看起來像是只改變了 p1 的  play 屬性,但其實是改變了父類  parent2 的 play 屬性,而p1,p2繼承了 parent2 ,所以p1,p2同時發生變化。

總結:

     優點:父類的方法(getName)得到了復用。
     缺點:重寫子類的原型 等於 父類的一個實例,(父類的實例屬性變成子類的原型屬性)如果父類包含引用類型的屬性,那么子類所有實例都會共享該屬性 (包含引用類型的*原型*屬性會被實例共享)。

3.組合方式

    function parent3() {
        this.name = 'parent3';
        this.play = [1, 2, 3];
    }

    function child3() {
        parent3.call(this);
        this.type = 'child3';
    }
    child3.prototype = new parent3();
    var p3 = new child3();
    var p4 = new child3();
    console.log(p3.play, p4.play);
    p3.play.push(4);
    console.log(p3,p4);
    console.log(p3.play, p4.play);

打印結果:

注意:

在上面的結果中,大家有沒有發現,同樣只給   p3.play.push(4) ,但是只有p3一個變了,但p4沒有變,其實大家通過小穎用紅框框起來的地方,大就會明白,為什么p3、p4的 __proto__ 都繼承了父類parent2 的屬性,為什么修改p3,p4,這次p4卻沒有變化。

總結:

     優點:繼承了上述兩種方式的優點,摒棄了缺點,復用了方法,子類又有各自的屬性。
     缺點:因為父類構造函數被執行了兩次,子類的原型對象(Sub.prototype)中也有一份父類的實例屬性,而且這些屬性會被子類實例(sub1,sub2)的屬性覆蓋掉,也存在內存浪費。

 4.組合繼承的優化1

    function parent4() {
        this.name = 'parent4';
        this.play = [1, 2, 3];
    }

    function child4() {
        parent4.call(this);
        this.type = 'child4';
    }
    child4.prototype = parent4.prototype;
    var p5 = new child4();
    var p6 = new child4();
    console.log(p5, p6);
    console.log(p5 instanceof child4, p5 instanceof parent4);
    console.log(p5.constructor);

打印結果:

注意:

instanceof constructor 都是用來判斷一個實例對象是不是這個構造函數的實例的。
不同點是:用constructor 比instanceof 更嚴謹,例如如果 A 繼承 B,B 繼承 C,A 生成的實例對象,用 instanceof 判斷與 A、B、C 的關系,都是 true。所以無法區分這個到底是 A、B、C 誰生成的實例。而constructor 是原型對象的一個屬性,並且這個屬性的值是指向創建當前實例的對象的。

console.log(p5 instanceof child4, p5 instanceof parent4); 執行結果一樣,而且 p5.constructor 竟然不是 child4 而是 parent4。

5.組合繼承的優化2 ——寄生組合式繼承

   function parent5() {
        this.name = 'parent5';
        this.play = [1, 2, 3];
    }

    function child5() {
        parent5.call(this);
        this.type = 'child5';
    }
    child5.prototype = Object.create(parent5.prototype);
    child5.prototype.constructor = child5;
    var p7 = new child5();
    var p8 = new child5();
    console.log(p7, p8);
    console.log(p7.constructor);

打印結果:

總結:

    組合繼承的缺點就是在繼承父類方法的時候調用了父類構造函數,從而造成內存浪費,並且找不到實例對象真正的 constructor

   那在復用父類方法的時候,使用Object.create方法也可以達到目的,沒有調用父類構造函數,並將子類的 prototype.constructor 屬性賦值為自己本身,則問題完美解決。

 


免責聲明!

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



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