JS-ES5模擬super與多級繼承(三)

參考文章git

  1. js多層繼承 super方法

參考文章1中提供了一個思路, _super不必定要是一個變量, 也能夠是一個函數, 只要它能返回咱們指望的父級對象就能夠了. 下面是我對它給出的源碼的一些修改和註釋, 另外有3個測試示例.github

/*
 * @author: general
 * @github: https://gist.github.com/generals-space/a75cfca06e1f8d463022e0e02446c363
 */
/*
 * 要想擁有_super()方法, 必須繼承SuperExtend類.
 * 注意: 
 * 1. inherits方法中不要再在assign時爲子類添加指向父類自己的屬性了, 會出問題的.
 * 2. 當須要使用_super()方法調用父類的某個方法時, 必需要保證子類有同名方法, 須要經過子類的方法調用父類方法才行
 */
function SuperExtend(){}
SuperExtend.prototype._super = function(){
    // caller調用者應該會是子類的成員方法對象, 或是子類構造函數自己
    var caller = arguments.callee.caller;
    // 這裏先獲得this所屬的構造函數類
    var chain = this.constructor;
    var parent = null;
    // 沿繼承鏈一直向上遍歷, 至少要遍歷到SuperExtend的第一個子類
    // 目標是**找到主調函數到底屬於繼承鏈上的哪一層級, 而後才能獲得這個調用者的父類, 也就是咱們須要的super對象**
    while(chain && chain.prototype){
        // 對象的隱式原型`__proto__`屬性是一個指針, 它指向**構造本對象的**, **構造函數類**, **的原型**.
        // 可是因爲inherits的自定義繼承機制, chain.__proto__指向的是父級構造函數類(chain自己爲子級構造函數類)
        parent = chain.__proto__;
        // 若是調用者正好是構造函數類自己, 說明是在構造函數類的函數體中調用的,
        // 直接返回父級構造函數類自己
        if(caller == chain) return parent;

        // 若是調用者不是子級構造函數類, 就應該是原型中的方法了.
        var props = Object.getOwnPropertyNames(chain.prototype);
        for(var i = 0; i < props.length; i ++){
            // 這裏雖然相等, 但有多是當前類從上一層父類繼承而來的屬性, 而當前類自己並無定義過這個方法.
            // 須要進一步確認, 即確認父類原型上沒有與它徹底相同的方法(固然, 方法名可能同樣).
            if(caller == chain.prototype[props[i]] && caller != parent.prototype[props[i]]){
                return parent.prototype;
            }
        }
        chain = parent;
    }
    return chain;
};
/*
 * function: 自定義通用繼承方法.
 * 使用方法: inherits(子類, 父類)
 */ 
function inherits(subClass, superClass){
    Object.assign(subClass.prototype, superClass.prototype, {
        constructor: subClass,
    });
    // 創建這種聯繫後, 至關於subClass成了superClass的實例了
    // 基本等價於subClass.prototype = superClass
    Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; 
}

3個測試示例以下函數

測試用例1. 基本測試

// 測試用例1. 基本測試
function A(a){
    this.a = a;
}

inherits(A, SuperExtend);

A.prototype.sayHi = function(){
    console.log(this.a);
};

function B(a, b){
    this._super().call(this, a);
    this.b = b;
}

inherits(B, A);

B.prototype.sayHi = function(){
    this._super().sayHi.call(this);
    console.log(this.a, this.b);
};

function C(a, b, c){
    // 這裏獲得的是父級構造函數類自己, 直接call調用便可
    this._super().call(this, a, b);
    this.c = c;
}

inherits(C, B);

C.prototype.sayHi = function(){
    // 這裏獲得的是父級構造函數類的原型對象
    this._super().sayHi.call(this);
    console.log(this.a, this.b, this.c);
};

var c = new C(2, 5, 8);
c.sayHi();

測試用例2. 驗證同層級函數間調用的狀況

// 測試用例2. 驗證同層級函數間調用的狀況
function A(a){
    this.a = a;
}

inherits(A, SuperExtend);

A.prototype.sayA = function(){
    this.sayB();
};

A.prototype.sayB = function(){
    console.log(this.a);
};

function B(a, b){
    this._super().call(this, a);
    this.b = b;
}

inherits(B, A);

B.prototype.sayB = function(){
    this._super().sayB.call(this);
    console.log(this.a, this.b);
};

function C(a, b, c){
    // 這裏獲得的是父級構造函數類自己, 直接call調用便可
    this._super().call(this, a, b);
    this.c = c;
}

inherits(C, B);

var c = new C(2, 5, 8);
c.sayA();

測試用例3. 驗證主調函數與被調函數不一樣名的狀況

// 測試用例3. 驗證主調函數與被調函數不一樣名的狀況
function A(a){
    this.a = a;
}

inherits(A, SuperExtend);

A.prototype.sayA = function(){
    console.log(this.a);    
};

function B(a, b){
    this._super().call(this, a);
    this.b = b;
}

inherits(B, A);

B.prototype.sayB = function(){
    this._super().sayA.call(this);
    console.log(this.a, this.b);
};

function C(a, b, c){
    // 這裏獲得的是父級構造函數類自己, 直接call調用便可
    this._super().call(this, a, b);
    this.c = c;
}

inherits(C, B);

var c = new C(2, 5, 8);
c.sayB();
相關文章
相關標籤/搜索