這篇文章,來源於一場事故...
關於,今天會寫這個小結,也是來源於一場小事故。爲了偷懶,就喜歡將一些方法,封裝起來,只暴露一個接口,通過傳入指定的參數,只調用一個API就能做成一件小事。那麼不可避免,我們就需要給這個方法設置一些默認的參數,然後通過傳入的參數去覆蓋它,或者通過它來寫一個jQuery的插件。於是,就理所當然的用了jQuery的extend().
相信大部分的人都非常的熟悉jQuery,也十分的熟悉jQuery用來合併對象和擴展方法的extend().但是,有些時候,我們在開發中,要把jQuery的功能用到淋漓盡致,充分發揮它的才能的時候,其實很少。我們不可能說只是爲了用jQuery的選擇器,或者只是爲了用它的extend方法,就把整個庫都引入我們的工程當中去,那真是十分耗資源。
於是先寫了個1.0的low版本用來擴展屬性
var Kingsley = function(){};
//v1.0
Kingsley.prototype.extend = fucntion(defaults,parameters){
for(var param in parameters){
if(defaults.hasOwnProperty(param) && parameters.hasOwnProperty(param)){
defaults[param] = parameters[param];
}
}
return defaults;
}
但是,很快就發現了不足。於是做了些改進,有了2.0版本
Kingsley.prototype.extend = function(){
var options,target,copy,src,
i = 1,
len = arguments.length;
target = arguments[0];
for(;i < len;i++){
if((options = arguments[i]) != null){
for(var param in options){
if(target.hasOwnProperty(param) && options.hasOwnProperty(param)){
src = target[param];
copy = options[param];
if(typeof src === "object"){
target[param] = Kingsley.prototype.extend(src,copy);
}else{
target[param] = copy;
}
}
}
}
}
return target;
}
2.0的版本看起來稍微有點起色了,但好像也不好,還是需要改進。
於是呢。。。鋪墊了這麼久,該上jQuery的源代碼了。
我們先來看
jQuery.extend = jQuery.fn.extend = function() {
var options, name, src, copy, copyIsArray, 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;
};
一看,就很容易看出區別了。jQuery的代碼寫的非常的優雅,而且做了許多了處理機制,防止意外發生。
調用jQuery的extend,除了可以擴展屬性,還能用來擴展jQuery的靜態方法和實例方法。
1.$.extend(true,defaults,options);
2.$.extend({say:function(){ alert("Hello World")}});
我們把它一部分一部分拆解開來
// Handle a deep copy situation
if ( typeof target === "boolean" ) {
deep = target;
// Skip the boolean and the target
target = arguments[ i ] || {};
i++;
}
很明顯 ,deep是我們傳入的第一個參數,這個參數也是爲了告訴jQuery,我們進行的是深拷貝還是淺拷貝。所謂的深拷貝,其實就是,當你進行多個參數的合併後,如果改變了某個對象的屬性的值,相應的對象的屬性會不會也發生了改變。深拷貝是相互獨立的。改變了一個屬性的值,並不會影響到另一個屬性的值。
// 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--;
}
上面這段代碼,只是做了個簡單的判斷。如果傳入的參數不是對象,或者不是一個函數,那麼就把它初始化爲一個空對象。而target = this,則是在只傳入一個對象的時候,把jQuery本身賦值給了target。這麼做的目的,就是爲了能夠在jQuery上擴展方法。
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;
}
}
}
}
後面的這一大串,大部分所作的事情,就是爲了將一個對象的屬性擴展到另一個對象的身上。其中,比較關鍵的是
// Prevent never-ending loop
if ( target === copy ) {
continue;
}
和// Never move original objects, clone them
target[ name ] = jQuery.extend( deep, clone, copy );
前者爲了防止死循環,而後者,則是在出現對象的屬性又是個對象的時候如何解決,那就是遞歸又調用了自己。
嗯。其實非常簡單,但是,能夠寫得優雅,其實並不容易。