Ext.extend用法以及代碼解讀

概述

Ext.extendExt的繼承機制,這個函數的代碼相當難懂。要明白這個函數的代碼,首先要知道這個函數如何使用。

使用方式

使用示例

假設有個function名為SuperClass,要實現一個子類,名為MyClass。下面的兩種方式都可以實現這個功能。

MyClass = Ext.extend(SuperClass, { /* */ });

Ext.extend(MyClass, SuperClass, { /* */});

 

下面來個具體示例:

var a = function(id){

    this.id = id;

}

a.prototype = {

    tostring : function(){

        return this.id;

    }

};

           

b = function(id){

    b.superclass.constructor.call(this, id);

}

Ext.extend(b, a, {

    tostring : function(){

        return String.format("b:{0}", this.id);

    }

});

//測試一下

var obj1 = new a("obj1");

alert(obj1.tostring());

var obj2 = new b("obj2");

alert(obj2.tostring());

或者下面的代碼,可以得到同樣的效果:

var a = function(id){

    this.id = id;

}

a.prototype = {

    tostring : function(){

       return this.id;

    }

};

 

b = Ext.extend(a, {

    tostring : function(){

       return String.format("b:{0}", this.id);

    }

});

//測試一下

var obj1 = new a("obj1");

alert(obj1.tostring());

var obj2 = new b("obj2");

alert(obj2.tostring());

 

一個錯誤例子

下面看個示例:

BaseClass = function() {

    this.f1 = function() {

        alert("f1 in base");

    }

 

    this.f2 = function() {

        alert("f2 in base");

    }

}

 

ChildClass = function() {

  ChildClass.superclass.constructor.call(this);

}      

 

Ext.extend(ChildClass, BaseClass, {

    f1: function() {

        alert("f1 in child");

    },

 

    f3: function() {

        alert("f3 in child");

    }

});

 

var b = new ChildClass();

b.f1();

b.f2();

b.f3();

 

可以去執行一下,可以發現f1的執行結果仍然是"f1 in base"。并沒有真正的達到override的效果。

 

Ext.extend puts the properties specified in the 3rd argument into the subclass's prototype

 

也就是說:第三個參數里面的函數被放置在了子類的prototype中。

而在ChildClass.superclass.constructor.call(this);這句上,BaseClassf1成了ChildClass的變量,而不是ChildClass.prototype。通過對JavaScript的原型繼承的了解,可以知道,實例變量的優先級是高于prototype的,所以上面的這個代碼是達不到override的功能的。

 

修改的方式如下:

BaseClass = function() {

};

 

BaseClass.prototype = {

    f1: function() {

        alert("f1 in base");

    }

};

 

代碼解讀

JavaScript中的繼承實現

先了解一下最簡單的繼承是如何實現的:

function Extend(subFn, superFn){

    subFn.prototype = new superFn()

    subFn.prototype.constructor = subFn

}

 

function Animal(){

    this.say1 = function(){

        alert("Animal");

    }

}

 

function Tiger(){

    this.say2 = function(){

        alert("Tiger");

    }

}

 

Extend(Tiger,Animal);

 

var tiger = new Tiger();

tiger.say1();//"Animal"

tiger.say2();//"Tiger"

 

可以看到最簡單的繼承只做了兩件事情,一是把subFnprototype設置為superFn的一個實例,然后設置subFn.prototype.constructorsubFn

 

Ext.extend的代碼

Ext.extend函數中用到了Ext.override,這個函數把第二個參數中的所有對象復制到第一個對象的prototype中。首先貼上Ext.override函數的代碼:

Ext.override = function(origclass, overrides){

    if(overrides){

       var p = origclass.prototype;

       for(var method in overrides){

           p[method] = overrides[method];

       }

    }

}

 

然后貼上Ext.extend的代碼:

 

/**

 * 繼承,并由傳遞的值決定是否覆蓋原對象的屬性

 * 返回的對象中也增加了override()函數,用于覆蓋實例的成員

 * @param {Object} subclass 子類,用于繼承(該類繼承了父類所有屬性,并最終返回該對象)

 * @param {Object} superclass 父類,被繼承

 * @param {Object} overrides (該參數可選) 一個對象,將它本身攜帶的屬性對子類進行覆蓋

 * @method extend

 */

function extend (){

    // inline overrides

    var io = function(o){

       for(var m in o){

           this[m] = o[m];

       }

    };

    return function(sb, sp, overrides){

       if(typeof sp == 'object'){

           overrides = sp;

           sp = sb;

           sb = function(){sp.apply(this, arguments);};

       }

       var F = function(){}, sbp, spp = sp.prototype;

       F.prototype = spp;

       sbp = sb.prototype = new F();

       sbp.constructor=sb;

       sb.superclass=spp;

       if(spp.constructor == Object.prototype.constructor){

           spp.constructor=sp;

       }

       sb.override = function(o){

           Ext.override(sb, o);

       };

       sbp.override = io;

       Ext.override(sb, overrides);

       return sb;

    };

}();

 

代碼中進行了太多的簡寫,看起來不是特別方便,把代碼中的簡寫補全,代碼如下:

function extend(){

    // inline overrides

    var inlineOverride = function(o){

        for (var m in o) {

            this[m] = o[m];

        }

    };

    return function(subFn, superFn, overrides){

        if (typeof superFn == 'object') {

           //如果subFn也是對象的話(一般來說subFn這里放的是父類的構造函數),那么第三個參數overrides參數相當于被忽略掉

            overrides = superFn;

            superFn = subFn;

           //subFn重新定義了函數

            subFn = function(){

                superFn.apply(this, arguments);

            };

        }

        var F = function(){

        }, subFnPrototype, superFnPrototype = superFn.prototype;

        F.prototype = superFnPrototype;

        subFnPrototype = subFn.prototype = new F();

       subFnPrototype.constructor = subFn;

       subFn.superclass = superFnPrototype;

       

       if (superFnPrototype.constructor == Object.prototype.constructor) {

            superFnPrototype.constructor = superFn;

        }

        subFn.override = function(obj){

            Ext.override(subFn, obj);

        };

        subFnPrototype.override = inlineOverride;

        Ext.override(subFn, overrides);

        return subFn;

    };

};

 

補全以后也不是特別容易明白,那么我們就把這個代碼分開,分為2個參數和3個參數。

 

兩個參數的Ext.extend代碼

首先把代碼改寫成兩個參數的。

//兩個參數的時候的代碼,注意第二個參數必須為object

function extend(){

    // inline overrides

    var inlineOverride = function(o){

        for (var m in o) {

            this[m] = o[m];

        }

    };

    return function(superFn, overrides){

       var subFn = function(){

           superFn.apply(this, arguments);

       };

 

        var F = function(){

        }, subFnPrototype, superFnPrototype = superFn.prototype;

      

        F.prototype = superFnPrototype;

       //注意下面兩句就是上面最簡單的繼承實現。

        subFnPrototype = subFn.prototype = new F();

       subFnPrototype.constructor = subFn;

       //添加了superclass屬性指向superFnPrototype

       subFn.superclass = superFnPrototype;

      

       //subFnsubFnPrototype添加override函數

        subFn.override = function(obj){

            Ext.override(subFn, obj);

        };

        subFnPrototype.override = inlineOverride;

       

       //覆蓋掉子類prototype中的屬性

        Ext.override(subFn, overrides);

        return subFn;

    };

};

從注釋中可以看到,做的工作很簡單,只是定義一個subFn函數,這個函數中會調用superFn函數。定義了subFn以后,就使用上面的最簡單的繼承方式實現繼承。然后為subFnsubFnprototype添加了一個override函數。最后的Ext.override(subFn, overrides);overrides中的函數寫入subFnprototype中。

 

三個參數的Ext.extend代碼

下面我們把函數改寫為只處理3個參數的,改寫后的代碼如下:

//三個參數時的代碼

function extend(){

    // inline overrides

    var inlineOverride = function(o){

        for (var m in o) {

            this[m] = o[m];

        }

    };

    return function(subFn, superFn, overrides){

        var F = function(){

        }, subFnPrototype, superFnPrototype = superFn.prototype;

      

        F.prototype = superFnPrototype;

       //注意下面兩句就是上面最簡單的繼承實現。

        subFnPrototype = subFn.prototype = new F();

       subFnPrototype.constructor = subFn;

       //添加了superclass屬性指向superFnPrototype

       subFn.superclass = superFnPrototype;

 

       //subFnsubFnPrototype添加override函數

        subFn.override = function(obj){

            Ext.override(subFn, obj);

        };

        subFnPrototype.override = inlineOverride;

       

       //覆蓋掉子類prototype中的屬性

        Ext.override(subFn, overrides);

        return subFn;

    };

};

 

過程與兩個參數的時候相差無幾,只是兩個參數的時候,subFn時重新定義的一個function,而三個參數的時候,這個步驟就省略了。

總結及說明

這樣大家就對這個函數很明白了吧,也可以知道Ext.extend的繼承只會覆寫構造函數prototype中的對象,使用的時候需要多加注意。

 

注意下面一段代碼:

if (superFnPrototype.constructor == Object.prototype.constructor) {

    superFnPrototype.constructor = superFn;

}

這段代碼我在改寫的Ext.extend中省略掉了。原因在于我嘗試了多次,發現參數為兩個參數的時候,只有第一個參數為Object對象或者為3個參數的時候,第二個參數為Object才會進入此段代碼。

但是發現superFn也時function Object(){},在IEFF下都是如此。那么我就不是很清楚這段代碼到底是什么用的了,若有清楚的,告訴一聲,哈。

 

上面的有幾段代碼源自網絡,若有版權侵犯,非常抱歉。