zoukankan      html  css  js  c++  java
  • 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;
    }

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

  • 相关阅读:
    忘记mysql的登陆密码该怎么办?
    localhost和127.0.0.1的区别
    什么是DAPP
    System.NullReferenceException:未将对象引用设置到对象的实例,这是一个新鸟,中鸟,老鸟都避不开的错误
    VS2015中常用快捷键的修改推荐
    Win10系统开启IIS服务步骤
    ASP.NET学习路线图
    我眼中的技术地图
    WEBSTORM中html文件运行之后出现乱码的问题解决
    ExtJS新手学习中常见问题
  • 原文地址:https://www.cnblogs.com/cyITtech/p/3667340.html
Copyright © 2011-2022 走看看