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、暴漏自己的不足。