JavaScript物件導向-- 繼承(Inherit) - IV

上篇只講完第1個條件, 接著講第2個條件。

一開始, 會覺得當然直接將子層Class的原型指向父層Class就好了, 但會發生call by sharing:

e.g.

function A() {

    this.abc = 12;

}

A.prototype.show = function() {

    console.log(this.abc);

};

function B() {

    A.call(this);

}

B.prototype = A.prototype;

var objA = new A();
var objB = new B();

B.prototype.square = function() {

console.log(this.abc * this.abc);

};

objA.show();            // 12
objB.show();            // 12
objB.square();         // 144
objA.square();        // 144 (照理說, 應該要是不存在, 但因有call by sharing,  因而當B.prototype = A.prototype時, 在B所做的任何動作, 在A都看的到)


因而, 我們的解法: 透過prototype建立一個新實體, 因為瀏覽器總是先從自己本身物件上找方法&屬性, 找不到才會往原型裡面找。 我們透過這種方式擴充子層Class的原型方法, 且不會去影響父層Class了。以下舉例:

e.g.

function A(abc) {

    this.abc = abc || 12;
    console.log("A class"); //繼承的時後會執行兩次

}

A.prototype.show = function() {

    console.log(this.abc);

};

function B() {

    A.apply(this, arguments);

}

B.prototype = new A();

var objA = new A();
var objB = new B(11);

B.prototype.square = function() {

    console.log(this.abc * this.abc);

};

objB.square(); //121
objA.square(); //objA.square is not a function, 成功, 擴充的子層Class的原型方法不影響父層Class了。


但從上面例子可知道, 當我利用父層Class建立的新實體當作子層Class的原型時, 會導致父層Class多被執行一次, 所以解法就如上篇的第2個條件一樣, 以下舉例:

e.g.

var util = {

    inherits: function(ctor, superCtor) {            // ctor為子層Class, superCtor為父層Class

        var F = function() {};                                    // 建立空的全新Class
        F.prototype = superCtor.prototype;      // 將空Class設定給父層Class原型
        ctor.prototype = new F();                         // 這樣new的是F, 不會重複執行父層Class, 且同時又能將父層Class的原型屬性&方法拿來使用。
        ctor.uber = superCtor.prototype;          // uber只是一個屬性, 可將父層Class屬性存入--> 當要使用的不是擴充的子層Class的原型屬性&方法時, 可以這麼用。
        ctor.prototype.constructor = ctor;        // 將constructor指向為自己(也就是子層Class), 這樣才不會迷路。

    }

};

function A(abc) {

    this.abc = abc || 12;
    console.log("A class");

}

A.prototype.show = function() {

    console.log(this.abc);

};

function B() {

    A.apply(this, arguments);

}

util.inherits(B, A);                                                     // B去繼承A的方法&屬性

var objA = new A();
var objB = new B();

B.prototype.square = function() {

    console.log(this.abc * this.abc);

};

objB.square();                                                         //144
objA.square();                                                        //objA.square is not a function



最後, 可以改良上面例子, 因為在JavaScript中看到的任何東西皆來自物件, 所以可替Object的原型建立繼承方法, 使得繼承關係更方便建立:

e.g.

Object.prototype.inherits = function(superCtor) {

    var F = function() {};
    F.prototype = superCtor.prototype;
    this.prototype = new F();
    this.uber = superCtor.prototype;
    this.prototype.constructor = this;

};

function A(abc) {

    this.abc = abc || 12;
    console.log("A class");

}

A.prototype.show = function() {

    console.log(this.abc);

};

function B() {

    A.apply(this, arguments);

}

B.inherits(A);

var objA = new A();
var objB = new B();

B.prototype.square = function() {

        console.log(this.abc * this.abc);

};

objB.square(); //144
objA.square(); //objA.square is not a function




終於將JavaScript繼承說完了!


p.s. JavaScript只有單一繼承, 其他語言有多重繼承。





留言

熱門文章