jquery 源码分析十一

这篇开始分析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;
}

这篇就主要先粗略的讲解下这些内容,在后面会继续分析这些函数的调用,详见后面的博客(马上要断网了,赶紧撤)

原文地址:https://www.cnblogs.com/cyITtech/p/3667340.html