jQuery 1.6 源码学习(六)——core.js[6]之jQuery对象/数组操作相关方法

本文将分析jQuery对象操作相关方法(包括静态和实例方法):

merge方法,代码如下:

 

	//此方法用于合并两个jQuery对象(因为jQuery对象中有length属性)或者数组,

	//这个方法非常简单,就是简单的追加第二个对象的属性到第一个对象上去

	merge: function( first, second ) {

		var i = first.length,

			j = 0;

		if ( typeof second.length === "number" ) {

			for ( var l = second.length; j < l; j++ ) {

				first[ i++ ] = second[ j ];

			}



		} else {

			while ( second[j] !== undefined ) {

				first[ i++ ] = second[ j++ ];

			}

		}



		first.length = i;



		return first;

	}, 

可以看到此方法非常简单,但它在jQuery内部应用的地方却是非常多的,包括init方法,pushStack方法,makeArray方法里都用到了此方法。

pushStack方法,代码如下:

 

 	pushStack: function( elems, name, selector ) {

		//constructor指向jQuery方法本身,那自然ret会被创建为一个空的jQuery对象(可作为一个元素集)

		//此时等效为 var ret = jQuery();

		// Build a new jQuery matched element set

		var ret = this.constructor();



		if ( jQuery.isArray( elems ) ) {

			//push是Array.prototype.push的一个shortcut,此时将elems添加到ret中

			push.apply( ret, elems );



		} else {

			jQuery.merge( ret, elems );

		}

		// 保存操作前的对象

		// Add the old object onto the stack (as a reference)

		ret.prevObject = this;



		ret.context = this.context;



		if ( name === "find" ) {

			ret.selector = this.selector + (this.selector ? " " : "") + selector;

		} else if ( name ) {

			ret.selector = this.selector + "." + name + "(" + selector + ")";

		}



		// Return the newly-formed element set

		return ret;

	},

要注意的是pushStack方法并不对源对象做破坏性更改(所谓破坏性更改,是指对原jQuery对象中选择的元素有变动的更改),而是返回新的 jQuery对象,因此在使用时需要注意。也就是说诸如下面的代码在使用时容易使人迷惑,其实obj并未把DOM元素加入到当前的jQuery栈,而我们 将元素加入当前jQuery栈的一般做法还是用选择器来实现:

 

	var obj = $("a");

	ret = obj.pushStack(document.getElementsByTagName("span")).pushStack(document.getElementsByTagName("input"));

	//obj依然是包含a元素的jQuery对象

	console.log(obj); //[<a>?</a>?]

	//ret则仅仅包含input

	console.log(ret); //[<input type=?"button" id=?"btn" value=?"click!">?] 

之所以把pushStack设置为实例方法而非静态方法,就是为了在jQuery中实现一个undo操作的功能,更多可以参见jQuery文档中关于end方法的使用。

其实pushStack本身在jQuery文档中也未公开,作为Internals方法存在,因此不建议使用此方法,但是在jQuery内部经常看到此方法出现,比如下面即将说到的map方法:

 

 	//该方法将返回经过callback过滤后的jQuery对象,

	map: function( callback ) {

		return this.pushStack( jQuery.map(this, function( elem, i ) {

			return callback.call( elem, i, elem );

		}));

	},

由于实例map方法调用了静态map方法,我们接着看看静态map方法:

 

	// arg is for internal usage only

	map: function( elems, callback, arg ) {

		var value, key, ret = [],

			i = 0,

			length = elems.length,

			// 传入jQuery对象时,将会把jQuery对象也作为数组来处理

			// jquery objects are treated as arrays

			isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ;



		// Go through the array, translating each of the items to their

		if ( isArray ) {

			for ( ; i < length; i++ ) {

				value = callback( elems[ i ], i, arg );//必须有返回值



				if ( value != null ) {

					ret[ ret.length ] = value;

				}

			}



		// Go through every key on the object,

		} else {

			for ( key in elems ) {

				value = callback( elems[ key ], key, arg );



				if ( value != null ) {

					ret[ ret.length ] = value;

				}

			}

		}

		// 最后连接数组,这样做是为了防止空项出现

		// Flatten any nested arrays

		return ret.concat.apply( [], ret );

	}, 

map方法和each方法非常的相似,其区别在于,map提供了一个数组到另一个数组映射的功能,而each则单纯地队对象或数组是进行迭代访问,这要求我们必须在使用map时,回调函数必须提供返回值,否则将在返回值中删除该项。

另一个容易混淆的数组遍历方法是grep:

 

	grep: function( elems, callback, inv ) {

		var ret = [], retVal;

		inv = !!inv;  //小提示,!!是为了确保inv转换为bool值,当不传递inv时,!!undefined便等于false



		// Go through the array, only saving the items

		// that pass the validator function

		for ( var i = 0, length = elems.length; i < length; i++ ) {

			retVal = !!callback( elems[ i ], i );

			if ( inv !== retVal ) {//默认回调函数返回false时将不保留此项

				ret.push( elems[ i ] );

			}

		}



		return ret;

	}, 

很容易知道我们在迭代数组时,必须要求回调函数中返回一个bool值来确定该项是否保留。

grep和map方法在文档中属于实用工具(Utilities),由于概念相近,因此放在这里讲解。

你可能感兴趣的:(JQuery对象)