對jQuery.extend()方法的分析


jQuery.extend方法是我們常用的方法,也是jQuery源碼中的基礎方法。它的主要作用是:將一個或多個“源對象”合並到一個“目標對象”中,並返回目標對象。它主要有三種表現形式:

a、jQuery.extend(destination, source1, source2, source3 ....)

b、jQuery.extend( source )

c、jQuery.extend(boolean, destination, source1, source2, source3 ....)

a方式第一個參數作為“目標對象”,其它參數作為“源對象”。

b方式只有一個參數,這里的這個參數變成了“源對象”,“目標對象”變成了jQuery。說白了就是"源對象"的屬性,變成jQuery函數的靜態方法或屬性。

c方式的第一個參數是boolean類型的,第二個參數是"目標對象",剩下的參數是“源對象”。當第一個參數的值為true時,表示對象合並時支持“深拷貝”。

知道了函數的用法,我們肯定好奇jQuery是怎么實現的,想看看jQuery的源碼。不過在看jQuery源碼之前,我們不妨試着寫寫這個方法的功能,然后在回過頭來看jQuery源碼,感受可能更深,看到的東西可能越多。

我們先不要給自己壓力,先從最簡單的開始,要實現的方法就兩個參數:第一個參數是:“目標對象”,第二個參數是:“源對象”。先實現把“源對象”合並到“目標對象”中。代碼如下:

var Test = function(){}
Test.extend0 = function(destination, source){
    for(var key in source){
        destination[key] = source[key]
    }
    return destination
}

第二步實現可以傳入多個參數,第一個參數是目標對象,其他參數是源對象。代碼如下:

Test.extend1 = function(){
    var destination = arguments[0]
    var sourceArr = Array.prototype.slice.call(arguments,1)
    for(var i = 0, len = sourceArr.length; i < len; i++){
        var source = sourceArr[i]
        for(var key in source){
            destination[key] = source[key]
        }
    }
    return destination
}

第三步實現只有一個參數時,將參數對象的屬性附加給Test。代碼如下:

Test.extend2 = function(){
    var argumentsLen = arguments.length
    if( argumentsLen === 1 ){
        var source = arguments[0]
        for(var key in source){
            Test[key] = source[key]
        }
    }else{
        var destination = arguments[0]
        var sourceArr = Array.prototype.slice.call(arguments,1)
        for(var i = 0, len = sourceArr.length; i < len; i++){
            var source = sourceArr[i]
            for(var key in source){
                destination[key] = source[key]
            }
        }
        return destination
    }
}

第四步實現“深拷貝”,第一個參數是是否進行深拷貝的布爾判斷,第二個參數是目標對象,其他參數是源對象。代碼如下:

Test.extend3 = function(){
    var argumentsLen = arguments.length
    if( argumentsLen === 1 ){
        var source = arguments[0]
        for(var key in source){
            Test[key] = source[key]
        }
    }else{
        var firstItem = arguments[0]
        var isBoolean = typeof firstItem === "boolean"
        var destination = isBoolean ? arguments[1] : firstItem
        var startNum = isBoolean ? 2 : 1
        var sourceArr = Array.prototype.slice.call(arguments,startNum)
        for(var i = 0, len = sourceArr.length; i < len; i++){
            var source = sourceArr[i]
            if( isBoolean ){
                deepExtend( destination, source )
            }else{
                for(var key in source){
                    destination[key] = source[key]
                }
            }
        }
        return destination
    }
}

function deepExtend(destination, source){
    for(var key in source){
        var value = source[key]
        if( value instanceof Array ){
            destination[key] = arguments.callee.call( destination[key] || [], value )
        }else if( value instanceof Object ){
            destination[key] = arguments.callee.call( destination[key] || {}, value )
        }else{
            destination[key] = source[key]                
        }
    }
    return destination
}

好了,我們按照自己的思路,粗略的實現了自己的extend方法,現在就看下jQuery對extend的實現,對比學習一下。源碼如下:

jQuery.extend = jQuery.fn.extend = function() {
    var src, copyIsArray, copy, name, options, clone,
        target = arguments[0] || {},
        i = 1,
        length = arguments.length,
        deep = false;

    // Handle a deep copy situation
    if ( typeof target === "boolean" ) {
        deep = target;

        // skip the boolean and the target
        target = arguments[ i ] || {};
        i++;
    }

    // Handle case when target is a string or something (possible in deep copy)
    if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
        target = {};
    }

    // extend jQuery itself if only one argument is passed
    if ( i === length ) {
        target = this;
        i--;
    }

    for ( ; i < length; i++ ) {
        // Only deal with non-null/undefined values
        if ( (options = arguments[ i ]) != null ) {
            // Extend the base object
            for ( name in options ) {
                src = target[ name ];
                copy = options[ name ];

                // Prevent never-ending loop
                if ( target === copy ) {
                    continue;
                }

                // Recurse if we're merging plain objects or arrays
                if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
                    if ( copyIsArray ) {
                        copyIsArray = false;
                        clone = src && jQuery.isArray(src) ? src : [];

                    } else {
                        clone = src && jQuery.isPlainObject(src) ? src : {};
                    }

                    // Never move original objects, clone them
                    target[ name ] = jQuery.extend( deep, clone, copy );

                // Don't bring in undefined values
                } else if ( copy !== undefined ) {
                    target[ name ] = copy;
                }
            }
        }
    }

    // Return the modified object
    return target;
}

通過對比,我們發現:

a、jQuery在代碼組織和實現上更加優雅。

b、jQuery考慮到了一些特殊情況。比如:

if ( target === copy ) {
      continue;
}

這是為了避免無限循環,“源對象”的屬性指向的是“目標對象”,當合並對象時,也就是將“自己”復制為“自己的屬性”。這是不可取的。

c、jQuery在數組(jQuery.isArray)和“純粹對象”(jQuery.isPlainObject)的判斷上,考慮的更精細。

先自己想思路去實現,再反過來對比學習,這種學習方法感覺挺好的。a、加強了獨立思考能力。b、發現新的學習內容。c、暴漏自己的不足。

 


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com