jQuery的extend方法

    這篇文章,來源於一場事故...

    關於,今天會寫這個小結,也是來源於一場小事故。爲了偷懶,就喜歡將一些方法,封裝起來,只暴露一個接口,通過傳入指定的參數,只調用一個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 );

前者爲了防止死循環,而後者,則是在出現對象的屬性又是個對象的時候如何解決,那就是遞歸又調用了自己。


嗯。其實非常簡單,但是,能夠寫得優雅,其實並不容易。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章