这篇开始分析Sizzle中的终极匹配器。
首先讲一下普通情况下,如:div a的匹配。在matcherFromToken函数中,可以看出匹配函数的组装方式,见源码:
if ( (matcher = Expr.relative[ tokens[i].type ]) ) { matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; } else { matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
根据不同的tokens.type来生成不同的匹配函数,匹配函数检测时从右到左,对于body div的情况,首先检测div。原理是将传入的element判断TAG是否符合,调用Filter中函数:
"TAG": function( nodeNameSelector ) { var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); return nodeNameSelector === "*" ? function() { return true; } : function( elem ) { return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; }; },
然后回到打包其的函数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函数中返回的函数的运行了,这个是由于body div中的' '形成的一个选择符函数,通过函数的运行,使得elem变量从原来传入的div元素,变为了body元素,并直接在此基础上运行下一步筛选函数,最后将结果缓存,返回。
function( elem, context, xml ) { var oldCache, outerCache, newCache = [ dirruns, doneName ]; // 不能再xml节点上设置额外信息,所以不能使用cache 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 ] = {}); if ( (oldCache = outerCache[ dir ]) && oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { // 有缓存且符合关系的话就直接返回不需调用matcher了 return (newCache[ 2 ] = oldCache[ 2 ]); } else { outerCache[ dir ] = newCache; // 在这里就开始运行下一步筛选函数了,即更深层嵌套的函数matcher if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) { return true; } } } } } };
然后追踪到matcher,可以发现其又进入了一个elementMatcher函数中,在这个函数中,我们观察matchers变量,可以发现其已经是到达最顶端了,matchers里主要是检测下一个是不是body元素,和document相关的东西:
按照从后到前的顺序,先运行检测body的函数。
===============================分割线=================================
以上是普通的情况,当设计到pseudos时,就会调用setMatcher来完成剩下的匹配,先上源码:
function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { // 对于在pseudos后面的选择符匹配函数 if ( postFilter && !postFilter[ expando ] ) { postFilter = setMatcher( postFilter ); } if ( postFinder && !postFinder[ expando ] ) { postFinder = setMatcher( postFinder, postSelector ); } return markFunction(function( seed, results, context, xml ) { var temp, i, elem, preMap = [], postMap = [], preexisting = results.length, // 得到初始匹配的elems elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), // 首先是将元素通过前置的筛选工作,得到pseudos处理前所有符合的函数 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 : matcherIn; // 找到主要的匹配项 if ( matcher ) { matcher( matcherIn, matcherOut, context, xml ); } // 调用postFilter if ( postFilter ) { temp = condense( matcherOut, postMap ); postFilter( temp, [], context, xml ); // 对于未匹配的元素,将其移到matcherIn i = temp.length; while ( i-- ) { if ( (elem = temp[i]) ) { matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); } } } if ( seed ) { if ( postFinder || preFilter ) { if ( postFinder ) { temp = []; i = matcherOut.length; while ( i-- ) { if ( (elem = matcherOut[i]) ) { // 恢复matcherIn temp.push( (matcherIn[i] = elem) ); } } postFinder( null, (matcherOut = []), temp, xml ); } // 将匹配的元素放入results,来保证两者同步 i = matcherOut.length; while ( i-- ) { if ( (elem = matcherOut[i]) && (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) { seed[temp] = !(results[temp] = elem); } } } // 将元素放入result中,通过postFinder(如果有定义的话) } else { matcherOut = condense( matcherOut === results ? matcherOut.splice( preexisting, matcherOut.length ) : matcherOut ); if ( postFinder ) { postFinder( null, results, matcherOut, xml ); } else { push.apply( results, matcherOut ); } } }); }
我们可以看到,其中调用了许多次condense函数来完成筛选,源码如下:
function condense( unmatched, map, filter, context, xml ) { var elem, newUnmatched = [], i = 0, len = unmatched.length, mapped = map != null; for ( ; i < len; i++ ) { if ( (elem = unmatched[i]) ) { if ( !filter || filter( elem, context, xml ) ) { newUnmatched.push( elem ); if ( mapped ) { map.push( i ); } } } } return newUnmatched; }
condense对符合filter的元素放入到返回中,并同时添加到传入的map中。
接下来就是生产最终匹配器的函数matcherFromGroupMatchers,源码如下:
function matcherFromGroupMatchers( elementMatchers, setMatchers ) { var bySet = setMatchers.length > 0, byElement = elementMatchers.length > 0, superMatcher = function( seed, context, xml, results, outermost ) { var elem, j, matcher, matchedCount = 0, i = "0", unmatched = seed && [], setMatched = [], contextBackup = outermostContext, // 必须有seed或者外围的context elems = seed || byElement && Expr.find["TAG"]( "*", outermost ), // 如果是outermost matcher的话,使用dirruns dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1), len = elems.length; if ( outermost ) { outermostContext = context !== document && context; } // 对于通过elementMatchers的元素,直接添加到result中 // 将i保持为字符串,这样如果没有元素的话,matchedCount就会显示为'00' 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; } } // 对于未匹配的元素尝试setMatcher if ( bySet ) { // 首先确保通过了所有的elementMatcher检验 if ( (elem = !matcher && elem) ) { matchedCount--; } // 放入unmatched if ( seed ) { unmatched.push( elem ); } } } // 使用setMatchers中的匹配对未匹配的进行检验 matchedCount += i; if ( bySet && i !== matchedCount ) { j = 0; while ( (matcher = setMatchers[j++]) ) { matcher( unmatched, setMatched, context, xml ); } if ( seed ) { // 将匹配element全部整合,准备用于排序 if ( matchedCount > 0 ) { while ( i-- ) { if ( !(unmatched[i] || setMatched[i]) ) { setMatched[i] = pop.call( results ); } } } // 丢弃index值,来保证拿到的只是匹配的元素 setMatched = condense( setMatched ); } // 然后将整合后的元素再放回到results中 push.apply( results, setMatched ); // Seedless set matches succeeding multiple successful matchers stipulate sorting if ( outermost && !seed && setMatched.length > 0 && ( matchedCount + setMatchers.length ) > 1 ) { Sizzle.uniqueSort( results ); } } // Override manipulation of globals by nested matchers if ( outermost ) { dirruns = dirrunsUnique; outermostContext = contextBackup; } return unmatched; }; return bySet ? markFunction( superMatcher ) : superMatcher; }
这篇就主要先粗略的讲解下这些内容,在后面会继续分析这些函数的调用,详见后面的博客(马上要断网了,赶紧撤)