jQuery源码分析之实例find和filter方法的区别七问

问题1:jQuery.filter的源码是什么?

jQuery.filter = function( expr, elems, not ) {
	var elem = elems[ 0 ];
	//如果含有第三个参数表示选择不满足特定条件的结果!
	if ( not ) {
		expr = ":not(" + expr + ")";
	}
	return elems.length === 1 && elem.nodeType === 1 ?
		//如果选择集合只有一个元素,调用matchesSelector,否则调用matches方法!
		jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] :
		jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) {
			return elem.nodeType === 1;
		}));
		//很显然返回的是集合!
};
很显然返回的是对象数组,同时第三个参数表示是否取反,grep第三个参数也是取反功能!

问题2:jQuery.filter调用方式有那些?

在prevAll,nextAll,siblings中调用

	ret = jQuery.filter( selector, ret );
例子:

 <div class="father" id="father" style="background-color:red;height:400px;background-color:#ccc;">  
</div>  
 <div class="father" style="background-color:red;height:400px;background-color:#ccc;">  
</div>  
JS部分

 //获取集合$("div")中满足class为father的元素
       var result=jQuery.filter(".father",$("div"));
       console.log(result);//打印[div#father.father, div.father]
因为jQuery.filter的第三个参数表示 是否取反,那么我们如何把它设置为true表示取反,也就是不满足selector的元素的集合

  //获取集合$("div")中满足class不为father的元素
       var result=jQuery.filter(".father",$("div"),true);
      console.log(result);//打印[]
通过源码很容易看到

if ( not ) {
		expr = ":not(" + expr + ")";//第三个参数表示对选择器进行取反,也就是添加:not!
	}
注意:jQuery.filter是对元素的集合进行筛选,所以返回的数组元素本来就在筛选之前的集合中!
问题3:上面是jQuery.filter方法,那么filter实例方法如何?

学习实例filter方法之前,我们先看看winnow方法

他的调用方式有以下

实例filter方法中:

 winnow(this, selector || [], false) 
实例not方法中:

 winnow(this, selector || [], true)
实例is方法中:

winnow(this,
// If this is a positional/relative selector, check membership in the returned set
// so $("p:first").is("p:last") won't return true for a doc with two "p".
	typeof selector === "string" && rneedsContext.test( selector ) ?jQuery( selector ) :selector || [],false)
现在我们看看winnow方法的源码:

function winnow( elements, qualifier, not ) {
	if ( jQuery.isFunction( qualifier ) ) {
		return jQuery.grep( elements, function( elem, i ) {
			/* jshint -W018 */
			return !!qualifier.call( elem, i, elem ) !== not;
		});
	}
	if ( qualifier.nodeType ) {
		return jQuery.grep( elements, function( elem ) {
			return ( elem === qualifier ) !== not;
		});
	}
	if ( typeof qualifier === "string" ) {
		if ( risSimple.test( qualifier ) ) {
			return jQuery.filter( qualifier, elements, not );
		}
		qualifier = jQuery.filter( qualifier, elements );
	}
	return jQuery.grep( elements, function( elem ) {
		return ( jQuery.inArray( elem, qualifier ) >= 0 ) !== not;
	});
}
如果是实例not,filter方法(传入字符串),那么执行下面的逻辑

      //在filter和not实例方法中,执行这里的逻辑
	if ( typeof qualifier === "string" ) {
		//var risSimple = /^.[^:#\[\.,]*$/;
		//如果是普通的选择器,那么我们用这个selector对数组进行筛选
		//对于not方法来说传入的true表示取反,而is实例方法不需要取反!
		if ( risSimple.test( qualifier ) ) {
			return jQuery.filter( qualifier, elements, not );
		}
		qualifier = jQuery.filter( qualifier, elements );
	}
所以还是调用jQuery.filter对 调用对象进行筛选,所以返回的对象 都是调用对象的一个子集!
如果是is方法,is()函数用于判断当前jQuery对象所匹配的元素是否符合指定的表达式。 只要其中有至少一个元素符合该表达式就返回true,否则返回false。这里的表达式包括:选择器(字符串)、DOM元素(Element)、jQuery对象、函数。 注意:is,not,filter方法都是可以传入字符串,DOM元素,jQuery对象,函数的,因为他们内部都是调用了winnow方法!
问题4:既然is,not,filter方法可以传入DOM,jQuery对象,函数对象,可以举例吗?

 <div class="father" id="father" style="background-color:red;height:400px;background-color:#ccc;">  
</div>  
 <div class="father" style="background-color:red;height:400px;background-color:#ccc;">  
</div>  
我们给filter传入DOM

     //我们知道is,not,filter都是可以传入DOM对象的
     var result=$("div").filter($("#father")[0]);
	 console.log(result);
    //打印[div#father.father, prevObject: jQuery.fn.init[2], context: document]
传入DOM,表示筛选指定的DOM元素,这时候返回的是含有指定DOM的jQuery对象

给is方法传入DOM

 //我们知道is,not,filter都是可以传入DOM对象的
     var result=$("div").is($("#father")[0]);
	 console.log(result);
   //打印true!
这时候打印true,只要调用对象含有参数对象,那么返回 布尔值true!
 //我们知道is,not,filter都是可以传入DOM对象的
     var result=$("div").not($("#father")[0]);
	 console.log(result);
	//打印[div.father, prevObject: jQuery.fn.init[2], context: document]!
这时候打印第二个div元素,表示返回除了指定元素外的其它的元素! 要注意:is方法返回的是布尔值!
传入的是DOM,那么在winnow函数中执行下面的逻辑:

	if ( qualifier.nodeType ) {
		//调用了grep方法,该方法为每一个元素调用指定的函数,如果该函数返回true
		//那么把该元素放入结果集合中(grep第三个参数在这里是false!)。
		return jQuery.grep( elements, function( elem ) {
			return ( elem === qualifier ) !== not;
		});
	}
如果是is或者filter方法,那么not是false,表示不用取反,这时候在上面这段代码中就是返回elem===qualifier的对象,也就是传入的参数对象,不同之处在于is已经转化为布尔值了 ! jQuery.grep的作用在于:选择调用指定函数后返回值是我们期望内向的元素(如果第三个参数是false表示返回调用函数返回true的元素,如果第三个参数是true表示返回调用函数的结果是false的元素)。如果是not方法,那么返回调用对象中不是指定元素的DOM元素!

问题5:is,not,filter方法中可以传入jQuery对象?

HTML还是和上面的例子一样
not实例方法

//我们知道is,not,filter都是可以传入jQuery对象的
     var result=$("div").not($("#father"));
	 console.log(result);
	//打印[div.father, prevObject: jQuery.fn.init[2], context: document]
is方法

 //我们知道is,not,filter都是可以传入jQuery对象的
     var result=$("div").is($("#father"));
	 console.log(result);
	//打印true
filter实例方法

//我们知道is,not,filter都是可以传入jQuery对象的
     var result=$("div").filter($("#father"));
	 console.log(result);
	//打印[div#father.father, prevObject: jQuery.fn.init[2], context: document]
这时候在winnow方法中执行下面的代码逻辑:

return jQuery.grep( elements, function( elem ) {
		return ( jQuery.inArray( elem, qualifier ) >= 0 ) !== not;
	});
这时候jQuery.grep第三个参数是false,表示返回执行指定函数后为true的DOM元素组成的数组。对于is,filter方法来说,not是false,那么表示,如果当前元素在参数也就是qualifier所在的集合中就会返回! 反过来想想,只要调用对象在参数集合中都会返回!如果是not实例方法,不在参数jQuery对象中的调用对象的所有的DOM都会返回!
问题6:is,not,filter方法中可以传入函数对象?
filter传入函数对象

 //我们知道is,not,filter都是可以传入函数对象的
     var result=$("div").filter(function(index,elem)
	 {
	    if(elem.id==="father")
		{
		  return false;
		  //不需要放入结果集合中就直接返回false,所以id=father元素不会在结果集合中
		}
		return true;//需要返回就return true!
	 });
	 //打印[div.father, prevObject: jQuery.fn.init[2], context: document]
	 console.log(result);
注意:传入的回调函数情况下,回调函数中第一个参数是下标,第二个参数是元素!
is方法传入函数:

  //我们知道is,not,filter都是可以传入函数对象的
     var result=$("div").is(function(index,elem)
	 {
	    if(elem.id==="father")
		{
		  return false;
		  //不需要放入结果集合中就直接返回false,所以id=father元素不会在结果集合中
		}
		return true;//需要返回就return true!
	 });
	 //打印true,因为对于第一个div元素执行函数返回了true!
	 console.log(result);
只要有一个函数调用后返回了true,那么结果就是true!
not方法传入函数:

     //我们知道is,not,filter都是可以传入函数对象的
     var result=$("div").not(function(index,elem)
	 {
	    if(elem.id==="father")
		{
		  return false;
		  //not方法如果返回false那么会在结果集合中!
		}
		return true;//返回true不再结果集合中!
	 });
	 //打印[div#father.father, prevObject: jQuery.fn.init[2], context: document]
	 console.log(result);
not方法传入函数,只要返回false就会在结果集合中,返回true不会在结果集合中!执行的代码逻辑是

if ( jQuery.isFunction( qualifier ) ) {
		//如果是函数参数,那么我们让每一个元素执行指定的函数!
		return jQuery.grep( elements, function( elem, i ) {
			/* jshint -W018 */
			return !!qualifier.call( elem, i, elem ) !== not;
		});
	}
实例not方法源码

not: function( selector ) {
		return this.pushStack( winnow(this, selector || [], true) );
	}
实例is方法源码

is: function( selector ) {
		return !!winnow(
			this,

			// If this is a positional/relative selector, check membership in the returned set
			// so $("p:first").is("p:last") won't return true for a doc with two "p".
			typeof selector === "string" && rneedsContext.test( selector ) ?
				jQuery( selector ) :
				selector || [],
			false
		).length;
	}
实例filter源码

filter: function( selector ) {
		return this.pushStack( winnow(this, selector || [], false) );//通过winnow方法选择成功过后我们进行入栈,返回是jQuery对象!
	}
问题7:我们不是讲实例filter方法和实例find方法的差别吗?看下面的差别?
首先来讨论一下find实例方法的作用:find()函数用于选取每个匹配元素的 符合指定表达式的后代元素,并以jQuery对象的形式返回。这里的表达式包括:选择器(字符串)、DOM元素(Element)、jQuery对象。

从这里你就会看到:虽然find方法和其它的方法一样可以传入DOM,jQuery,字符串(find方法无法传入函数),但是find方法返回的是调用对象满足指定选择器的子元素,而filter方法返回的是调用对象的一个集合!

jQuery.find = Sizzle;
我们知道最终还是调用了Sizzle的相关方法
如果我们传入了DOM或者jQuery对象

                       var i,
			ret = [],
			self = this,
			len = self.length;
            //如果find方法传入DOM,jQuery等执行这里的逻辑
		if ( typeof selector !== "string" ) {
			//所以如果是DOM,jQuery最终还是调用了filter实例方法
			//不过,这时候成了判断调用对象是否包含参数对象,如果包含就直接返回!
			//记住:这时候返回的是参数对象,参数对象扮演了子元素的角色
			return this.pushStack( jQuery( selector ).filter(function() {
				for ( i = 0; i < len; i++ ) {
					if ( jQuery.contains( self[ i ], this ) ) {
						return true;
					}
				}
			}) );
		}
通过上面的代码,虽然find方法不能传入函数,但是如果传入了DOM/jQuery对象,那么参数扮演的就是子元素的角色, 该方法返回的是子元素!
     var result=$("div").find($("#father"));
	 //打印[div#father.father, prevObject: jQuery.fn.init[2], context: document]
	 //返回的是子元素!
	 console.log(result);
判断$("div")这个调用对象集合中是否有子元素的id=father,如果有,那么返回!
如果我们传入了字符串的选择器:

          //如果是字符串对象的选择器,调用Sizzle,把所有的结果放入ret集合中!
		for ( i = 0; i < len; i++ ) {
			jQuery.find( selector, self[ i ], ret );
		}
		// Needed because $( selector, context ) becomes $( context ).find( selector )
		//如果调用对象的DOM对象有多个,那么调用jQuery.unique来对集合进行去重,如果只有一个DOM不用去重!
		//这时候返回的ret已经是jQuery对象了!
		ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret );
		//这时候要对selector进行更新,$("div").find(".name")那么返回的对象就是通过双重选择来完成的
		//变成("div .name")
		ret.selector = this.selector ? this.selector + " " + selector : selector;
		return ret;
注意:通过find方法返回的是jQuery对象。我们看看下面的代码:

		ret.selector = this.selector ? this.selector + " " + selector : selector;
通过下面的代码:

var result=$("div").find($("#father"));
	 //打印[div#father.father, prevObject: jQuery.fn.init[2], context: document]
	 //返回的是子元素!
	 console.log(result);
这时候返回的对象result的selector是"div#father"也就是双重选择的结果了!

总结:

(1)filter方法把this对象也就是调用对象传入winnow函数,同时把参数也就是选择器也传入winnow,在winnow里面调用jQuery.filter按照指定的选择器进行筛选,返回的是调         用对象个部分元素

(2)对于find方法,如果传入的对象不是string,如DOM对象等,那么通过contains或者compareDocumentPosition来判断。其中参数对象扮演的角色就是子元素,只要有调用对象包含着这个参数对象,就会把参数对象保存起来,所以该方法保存的是子元素!

 (3)区别:filter返回调用对象自己,find方法返回子元素

你可能感兴趣的:(jQuery源码分析之实例find和filter方法的区别七问)