在上一章中,咱們說到在以前的查找階段咱們已經得到了待選集seed,那麼這一章咱們就來說如何將seed待選集過濾,以得到咱們最終要用的元素。javascript
其實思路本質上仍是不停地根據token過濾,但compile這個函數將這些matcher(filter生成的閉包過濾函數)給編譯成一個函數(這個效率和咱們直接使用過濾函數差很少,關鍵是在後面),再保存這一個函數,之後遇到一樣的selector就能夠不用再編譯,直接調用就能夠了。java
接下來咱們看看compile的代碼node
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類型的matcherapp
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時傳入的參數一塊兒看less
//這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合併成一個超級matcherdom
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中調用)ide
compile( selector, match )( seed, context, !documentIsHTML, results, rsibling.test( selector ) && testContext( context.parentNode ) || context );
Sizzle源碼基本就是這樣了,接下來的文章咱們會繼續分析jQuery其餘模塊。