zoukankan      html  css  js  c++  java
  • jQuery1.11源码分析(5)-----Sizzle编译和过滤阶段[原创]

    在上一章中,我们说到在之前的查找阶段我们已经获得了待选集seed,那么这一章我们就来讲如何将seed待选集过滤,以获得我们最终要用的元素。

    其实思路本质上还是不停地根据token过滤,但compile这个函数将这些matcher(filter生成的闭包过滤函数)给编译成一个函数(这个效率和我们直接使用过滤函数差不多,关键是在后面),再保存这一个函数,以后遇到同样的selector就可以不用再编译,直接调用就可以了。

    接下来我们看看compile的代码

    compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) {
        console.log('compile begin');
        console.log('arguments:selector, group');
        console.log(arguments);
    	var i,
    		setMatchers = [],
    		elementMatchers = [],
    		cached = compilerCache[ selector + " " ];
    
    	if ( !cached ) {
    		// Generate a function of recursive functions that can be used to check each element
    		if ( !group ) {
    			group = tokenize( selector );
    		}
    		i = group.length;
    		while ( i-- ) {
                console.log('compile matcherFromTokens '+i);
    			cached = matcherFromTokens( group[i] );
                console.log('compile after matcherFromTokens '+i);
                console.log([cached]);
    			if ( cached[ expando ] ) {
                    //这里的区别是,setMatchers是当有伪类进行过递归调用Sizzle时出现的多层次的matcher
    				setMatchers.push( cached );
    			} else {
    				elementMatchers.push( cached );
    			}
    		}
    
    		// Cache the compiled function
            console.log('compile matcherFromGroupMatchers');
    		cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
            console.log('compile after matcherFromGroupMatchers');
            console.log([cached]);
    
    	}
    	return cached;
    };
    

    可以看出,compile实际上就是将不同的tokens用matcherFromTokens编译成一个个matcher(两种不同的matcher,setMatcher和elementMatcher),最后再调用matcherFromGroupMatchers,生成一个superMatcher。

    我们接下来看看matcherFromTokens和matcherFromGroupMatchers的源码(注意它是在什么时候把expando加上的,可能还要回到前几篇去看)

    function matcherFromTokens( tokens ) {
        console.log('matcherFromTokens begin');
        console.log('arguments:tokens');
        console.log(arguments);
        console.log('matcherFromTokens addCombinator');
    	var checkContext, matcher, j,
    		len = tokens.length,
    		leadingRelative = Expr.relative[ tokens[0].type ],
    		implicitRelative = leadingRelative || Expr.relative[" "],
    		i = leadingRelative ? 1 : 0,
    
    		// The foundational matcher ensures that elements are reachable from top-level context(s)
    		matchContext = addCombinator( function( elem ) {
    			return elem === checkContext;
    		}, implicitRelative, true ),
    		matchAnyContext = addCombinator( function( elem ) {
    			return indexOf.call( checkContext, elem ) > -1;
    		}, implicitRelative, true ),
    		matchers = [ function( elem, context, xml ) {
                console.log('matchers 1 begin');
                console.log('arguments:elem, context, xml');
                console.log(arguments);
    			return ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
    				(checkContext = context).nodeType ?
    					matchContext( elem, context, xml ) :
    					matchAnyContext( elem, context, xml ) );
    		} ];
    
    	for ( ; i < len; i++ ) {
    		if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
                console.log('matcherFromTokens addCombinator '+i);
                console.log('matcherFromTokens addCombinator elementMatcher '+i);
    			matchers = [ addCombinator(elementMatcher( matchers ), matcher) ];
                console.log('matcherFromTokens after addCombinator '+i);
                console.log(matchers);
    		} else {
                //如果不是连接符
                console.log('matcherFromTokens filter '+i);
    			matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
                console.log('matcherFromTokens after filter '+i);
                console.log(matchers);
    			// Return special upon seeing a positional matcher
                //expando说明什么?
                //在上面tokens[i].type为child或者pseudo时,matcher有[expando]
                //所以有expando的时候就要加强处理
    			if ( matcher[ expando ] ) {
    				// Find the next relative operator (if any) for proper handling
    				j = ++i;
    				for ( ; j < len; j++ ) {
    					if ( Expr.relative[ tokens[j].type ] ) {
    						break;
    					}
    				}
                    //prefilter,selector,matcher,postFilter,postFinder,postSelector;
                    //先看这里传入的参数,对于理解setMatcher非常有帮助,它说明matcherFromTokens用了递归的思想,把tokens切割成两部分,已匹配过的和待查找的
                    console.log('matcherFromTokens setMatcher');
    				return setMatcher(
    					i > 1 && elementMatcher( matchers ),
    					i > 1 && toSelector(
    						// If the preceding token was a descendant combinator, insert an implicit any-element `*`
    						tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" })
    					).replace( rtrim, "$1" ),
    					matcher,
    					i < j && matcherFromTokens( tokens.slice( i, j ) ),
    					j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
    					j < len && toSelector( tokens )
    				);
    			}
    			matchers.push( matcher );
    		}
    	}
        console.log('matcherFromTokens elementMatcher');
    	return elementMatcher( matchers );
    }
    

    在matcherFromTokens里用到了三个函数,addCombinator,setMatcher和elementMatcher,后两者的区别在前面的注释中已经有提及了,当不涉及递归等操作时,使用的就是普通的elementMatcher和addCombinator,elementMatcher的代码非常简单,如下

    function elementMatcher( matchers ) {
    	return matchers.length > 1 ?
    		function( elem, context, xml ) {
    			var i = matchers.length;
    			while ( i-- ) {
    				if ( !matchers[i]( elem, context, xml ) ) {
    					return false;
    				}
    			}
    			return true;
    		} :
    		matchers[0];
    }
    

    再看一个短函数addCombinator,增加一个Combinator类型的matcher

    function addCombinator( matcher, combinator, base ) {
    	var dir = combinator.dir,
    		checkNonElements = base && dir === "parentNode",
    		doneName = done++;
        //有first代表只检查第一个元素
    	return combinator.first ?
    		// Check against closest ancestor/preceding element
    		function( elem, context, xml ) {
    			while ( (elem = elem[ dir ]) ) {
    				if ( elem.nodeType === 1 || checkNonElements ) {
    					return matcher( elem, context, xml );
    				}
    			}
    		} :
    
    		// Check against all ancestor/preceding elements
    		function( elem, context, xml ) {
    			var oldCache, outerCache,
                //保证当次dirruns,doneName不变
    				newCache = [ dirruns, doneName ];
    
    			// We can't set arbitrary data on XML nodes, so they don't benefit from dir caching
    			if ( xml ) {
    				while ( (elem = elem[ dir ]) ) {
    					if ( elem.nodeType === 1 || checkNonElements ) {
    						if ( matcher( elem, context, xml ) ) {
    							return true;
    						}
    					}
    				}
    			} else {
    				while ( (elem = elem[ dir ]) ) {
    					if ( elem.nodeType === 1 || checkNonElements ) {
    						outerCache = elem[ expando ] || (elem[ expando ] = {});
                            //这里的outerCache也就是一个对象啊?怎么会有dir属性呢,是后面存进去的
    						if ( (oldCache = outerCache[ dir ]) &&
    							oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) {
    
    							// Assign to newCache so results back-propagate to previous elements
    							return (newCache[ 2 ] = oldCache[ 2 ]);
    						} else {
    							// Reuse newcache so results back-propagate to previous elements
                                //缓存最让我担心的还是失效时机
    							outerCache[ dir ] = newCache;
    
    							// A match means we're done; a fail means we have to keep checking
                                //这里我感觉这个函数的结构设计和matcher是紧耦合的。
    							if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) {
    								return true;
    							}
    						}
    					}
    				}
    			}
    		};
    }
    

    再来看最长的matcher。。。一定要结合上面调用setMatcher时传入的参数一起看

    //这TM又是一个奇葩的函数。
    //返回一个添加了expando的函数
    //让我难以理解的是filter和matcher这两种类型的函数有什么区别,没区别,filter生成matcher
    //第1个参数,preFilter,前置过滤器,相当于“div”过滤器
    //第2个参数,selector,前置过滤器的字符串格式,相当于“div”input:checked + p
    //第3个参数,matcher,当前位置伪类“:first”的匹配器/过滤器
    //第4个参数,postFilter,后置过滤器,相当于“ ”
    //第5个参数,postFinder,后置搜索器,相当于在前边过滤出来的集合里边再搜索剩下的规则的一个搜索器
    //第6个参数,postSelector,后置搜索器对应的选择器字符串,相当于“input:checked + p”
    //伪类选择器时会执行这个函数
    function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
    	if ( postFilter && !postFilter[ expando ] ) {
    		postFilter = setMatcher( postFilter );
    	}
    	if ( postFinder && !postFinder[ expando ] ) {
    		postFinder = setMatcher( postFinder, postSelector );
    	}
        //遇到这种闭包函数,要注意对上面参数的使用
        //这里的seed和results有什么区别?
        //results用来存已经可以确定返回的元素
    	return markFunction(function( seed, results, context, xml ) {
    		var temp, i, elem,
    			preMap = [],
    			postMap = [],
    			preexisting = results.length,
    
    			// Get initial elements from seed or context
                //这里如果没有seed,则获得所有context下的符合selector或*的元素
    			elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),
    
    			// Prefilter to get matcher input, preserving a map for seed-results synchronization
                //这行代码执行完后,matcherIn里剩下的元素是elems里通过preFilter过滤的,preMap存的是过滤通过的元素在原elems里的序号,从小到大
    			matcherIn = preFilter && ( seed || !selector ) ?
    				condense( elems, preMap, preFilter, context, xml ) :
    				elems,
    
    			matcherOut = matcher ?
    				// If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
                    //??????
    				postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
    
    					// ...intermediate processing is necessary
    					[] :
    
    					// ...otherwise use results directly
    					results :
                    //如果没有matcher,matcherOut就是matcherIn
    				matcherIn;
    
    		// Find primary matches
    		if ( matcher ) {
                //????matcher为什么会传入4个参数?看之前的声明只有3个
    			matcher( matcherIn, matcherOut, context, xml );
    		}
    
    		// Apply postFilter
            //应用尾过滤
    		if ( postFilter ) {
                //这里temp基本就是不做任何处理拷贝过来啊
    			temp = condense( matcherOut, postMap );
    			postFilter( temp, [], context, xml );
    
    			// Un-match failing elements by moving them back to matcherIn
    
    			i = temp.length;
    			while ( i-- ) {
    				if ( (elem = temp[i]) ) {
                        //这个temp果然是中介,把这些没用到的元素再覆盖到原matcherIn,按照postMap从大到小的顺序,再把matcherOut中的这部分设为false
    					matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
    				}
    			}
    		}
            //如果还剩seed
    		if ( seed ) {
    			if ( postFinder || preFilter ) {
                    //如果有postFinder?
    				if ( postFinder ) {
    					// Get the final matcherOut by condensing this intermediate into postFinder contexts
    					temp = [];
    					i = matcherOut.length;
    					while ( i-- ) {
    						if ( (elem = matcherOut[i]) ) {
    							// Restore matcherIn since elem is not yet a final match
    							temp.push( (matcherIn[i] = elem) );
    						}
    					}
                        //这是一个递归调用的方式
    					postFinder( null, (matcherOut = []), temp, xml );
    				}
    
    				// Move matched elements from seed to results to keep them synchronized
                    //这里有一个两个数组互斥的用法
    				i = matcherOut.length;
    				while ( i-- ) {
    					if ( (elem = matcherOut[i]) &&
    						(temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) {
    
    						seed[temp] = !(results[temp] = elem);
    					}
    				}
    			}
    
    		// Add elements to results, through postFinder if defined
    		} else {
    			matcherOut = condense(
    				matcherOut === results ?
    					matcherOut.splice( preexisting, matcherOut.length ) :
    					matcherOut
    			);
    			if ( postFinder ) {
    				postFinder( null, results, matcherOut, xml );
    			} else {
    				push.apply( results, matcherOut );
    			}
    		}
    	});
    }
    

    matcherFromTokens通过调用上面三个函数,然后生成了一个个matchers数组,然后compile再调用matcherFromGroupMatchers把这些matchers合并成一个超级matcher

    function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
        console.log('matcherFromGroupMatchers begin');
        console.log('arguments:elementMatchers, setMatchers');
        console.log(arguments);
    	var bySet = setMatchers.length > 0,
    		byElement = elementMatchers.length > 0,
    		superMatcher = function( seed, context, xml, results, outermost ) {
                console.log('superMatcher begin');
                console.log('arguments:seed, context, xml, results, outermost');
                console.log(arguments);
    			var elem, j, matcher,
    				matchedCount = 0,
    				i = "0",
    				unmatched = seed && [],
    				setMatched = [],
    				contextBackup = outermostContext,
    				// We must always have either seed elements or outermost context
    				elems = seed || byElement && Expr.find["TAG"]( "*", outermost ),
    				// Use integer dirruns iff this is the outermost matcher
    				dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1),
    				len = elems.length;
    
    			if ( outermost ) {
    				outermostContext = context !== document && context;
    			}
    
    			// Add elements passing elementMatchers directly to results
    			// Keep `i` a string if there are no elements so `matchedCount` will be "00" below
    			// Support: IE<9, Safari
    			// Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id
    			for ( ; i !== len && (elem = elems[i]) != null; i++ ) {
    				if ( byElement && elem ) {
    					j = 0;
    					while ( (matcher = elementMatchers[j++]) ) {
    						if ( matcher( elem, context, xml ) ) {
    							results.push( elem );
    							break;
    						}
    					}
    					if ( outermost ) {
    						dirruns = dirrunsUnique;
    					}
    				}
    
    				// Track unmatched elements for set filters
                    //???????这里有什么用?
    				if ( bySet ) {
    					// They will have gone through all possible matchers
    					if ( (elem = !matcher && elem) ) {
    						matchedCount--;
    					}
    
    					// Lengthen the array for every element, matched or not
    					if ( seed ) {
    						unmatched.push( elem );
    					}
    				}
    			}
    
    			// Apply set filters to unmatched elements
    			matchedCount += i;
    			if ( bySet && i !== matchedCount ) {
    				j = 0;
    				while ( (matcher = setMatchers[j++]) ) {
                        console.log('matcherFromGroupMatchers matcher '+j);
    					matcher( unmatched, setMatched, context, xml );
                        console.log('matcherFromGroupMatchers after matcher '+j);
                        console.log('setMatched');
                        console.log(setMatched);
                        console.log('unmatched');
                        console.log(unmatched);
    				}
    
    				if ( seed ) {
    					// Reintegrate element matches to eliminate the need for sorting
    					if ( matchedCount > 0 ) {
    						while ( i-- ) {
    							if ( !(unmatched[i] || setMatched[i]) ) {
    								setMatched[i] = pop.call( results );
    							}
    						}
    					}
    
    					// Discard index placeholder values to get only actual matches
                        //这里有毛用啊?
    					setMatched = condense( setMatched );
    				}
    
    				// Add matches to results
                    //注意这里用的是apply
                    //我TM终于理解为什么会有call和apply这两种语法了。。
    				push.apply( results, setMatched );
    
    				// Seedless set matches succeeding multiple successful matchers stipulate sorting
                    //没有待选元素了,就可以去除结果里重复的元素了
    				if ( outermost && !seed && setMatched.length > 0 &&
    					( matchedCount + setMatchers.length ) > 1 ) {
                        console.log('matcherFromGroupMatchers uniqueSort');
    					Sizzle.uniqueSort( results );
                        console.log('matcherFromGroupMatchers after uniqueSort');
                        console.log(results);
    				}
    			}
    
    			// Override manipulation of globals by nested matchers
    			if ( outermost ) {
    				dirruns = dirrunsUnique;
    				outermostContext = contextBackup;
    			}
    
    			return unmatched;
    		};
    
    	return bySet ?
    		markFunction( superMatcher ) :
    		superMatcher;
    }
    

    嗯,拿到这个superMatcher,剩下的就是调用了,最后我们看看传进去的参数(在select中调用)

    	compile( selector, match )(
    		seed,
    		context,
    		!documentIsHTML,
    		results,
    		rsibling.test( selector ) && testContext( context.parentNode ) || context
    	);
    

    Sizzle源码基本就是这样了,接下来的文章我们会继续分析jQuery其他模块。

  • 相关阅读:
    MyBatis——调用存储过程
    企业信息化快速开发平台JeeSite
    JavaWeb网页聊天室(WebSocket即时通讯)
    Java用webSocket实现tomcat的日志实时输出到web页面
    Java用WebSocket + tail命令实现Web实时日志
    linux 跨IP拷贝命令 scp
    在map中根据value获取key
    mysql 常用函数
    Nexus中自定义私服,每个项目都用独立的工厂,仓库
    button 默认类型是submit
  • 原文地址:https://www.cnblogs.com/suprise/p/3624955.html
Copyright © 2011-2022 走看看