首先来一段测试代码:
<div id="test"> <div id="child"></div> </div>JS部分:
//这里要记住了:通过源码分析,如果length不在有效范围之内那么传入pushStack //的参数是[]也就是空对象,所以返回空的jQuery对象! //打印:function(a,b){return new m.fn.init(a,b)} //$("div").constructor.length表示形参的个数 alert("选择器构造函数:"+$("div").constructor); //打印[object HTMLDocument] alert("选择器上下文:"+$("child").context); //下面打印true,说明选择器返回对象的contructor是jQuery对象 alert($("div").constructor() instanceof jQuery); //merge函数调用之前不是jQuery对象,这是和get或者下标访问一样的结果只是DOM alert("merge函数调用之前->"+([$("div")[0]] instanceof jQuery)) //返回false //merge函数通过把DOM对象的属性逐个封装到jQuery对象上,构建jQuery对象返回 var ret = jQuery.merge( $("div").constructor(), [$("div")[0]]); alert("merge函数调用后->"+(ret instanceof jQuery)); //打印true,merge后已经是jQuery对象了 //这里为undefined,因为我是直接merge,没有调用pushStack,所以没有封装到prevObject。调用eq就相当于调用pushStack方法了! alert("新jQuery返回旧的jQuery"+(ret.prevObject)); //也就是如果需要访问原来的选择器结果对象,如$("p")那么应该用prevObject //下面返回true alert("调用eq后就会有prevObject->"+($("div").eq(0).prevObject instanceof jQuery));
测试代码2:
//下面返回“test”,也就是说调用eq后如果要获取到上一次的选择结果,就可以用prevObject。因为调用eq的时候通过prevObject保存了上一次的this, //也就是选择的jQuery对象。$("div").eq(0).prevObject相当于$("div") alert($("div").eq(0).prevObject.eq(0).attr("id"));
测试代码3:
alert($("p").eq(1).constructor); // 打印:function(a,b){return new m.fn.init(a,b)}
eq源码分析:
eq: function( i ) { var len = this.length, j = +i + ( i < 0 ? len : 0 );//可以传入数字字符串!因为+就是将他变成数字! //传入的参数是[this[j]]=[DOM元素]=[$("p")[0]] return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] ); }
因为eq方法调用了pushStack所以他的返回值类型是jQuery类型;同时在pushStack中调用了jQuery.merge里面把所有的这里的DOM数组中的对象封装到一个空的jQuery对象上面并且返回!
pushStack源码:关于context详见codeplayer
pushStack: function( elems ) { // Build a new jQuery matched element set //创建新的jQuery对象集合 var ret = jQuery.merge( this.constructor(), elems ); // Add the old object onto the stack (as a reference) //用prevObject保存选择器的结果,也就是上面的eq函数的this对象 ret.prevObject = this; //保存上下文,默认是document ret.context = this.context //eq方法归根到底还是通过下标访问来完成的,通过把访问的下标的DOM对象逐个封装到空的JQuery对象中完成新的jQuery对象的构建! // Return the newly-formed element set return ret; }
get方法源码:
get: function( num ) { return num != null ? // Return just the one element from the set ( num < 0 ? this[ num + this.length ] : this[ num ] ) : //如果没有参数,如$("ul li").get()调用,那么this就是指$("ul li")的选择结果是jQuery对象,通过slice后返回的对象是Array // Return all the elements in a clean array slice.call( this ); }
我们看到如果调用get方法时候没有传入参数,那么调用了[].slice.call方法,从而把类数组的jQuery对象转化为了Array类型了!
测试例子:
//返回false,get方法返回的是DOM对象 alert($("ul li").get() instanceof jQuery); alert($("ul li").get()); //返回[object HTMLLiElement],[object HTMLLiElement],[object HTMLLiElement] //没有参数返回是数组,结果为true alert($("ul li").get() instanceof Array); //get没有参数的时候,this是jQuery对象,也就是选择器的结果如$("ul li") //通过slice就可以把jQuery对象$("ul li")转化为JS对象.下面返回都是true alert(Array.prototype.slice.call($("ul li")) instanceof Array); alert(Array.prototype.slice.call($("#me")) instanceof Array);总结:
(1)通过源码分析,传入到eq函数的length不在有效范围之内那么传入pushStack的参数是[]也就是空对象! 这时候返回的jQuery对象不包含任何DOM对象!
(2)var ret = jQuery.merge( $("div").constructor(), [$("div")[0]]); 可以把后面数组中的对象全部封装到前面的空的jQuery对象上面,构成一个新的jQuery对象!
(3)对于get方法我们通过是下标来访问的,如果没有传入参数那么通过slice返回整个调用对象的DOM集合,否则返回特定的项。如果是负数,那么eq和get是一样的,都是通过加上调用对象的长度!