jQuery源码笔记.jpg

jQuery源码#3 Sizzle 处理Token序列

2020-04-15  本文已影响0人  柠檬果然酸

本节内容
1.Sizzle.select

如何处理Token序列
Sizzle引擎没有直接从HTML获取DOM的手段,它是通过浏览器接口获取DOM对象。浏览器接口有4种:getElementById、getElementsByClassName、getElementsByName、getElementsByTagName,所以Sizzle只能处理ID,CLASS,TAG这3中情况。

总结下来:将Token序列从后往前查找,找到ID,CLASS,TAG中的一种便通过浏览器接口获取DOM对象,并且将获取到的对象保存在seed种子集合中。

Sizzle.select
寻找到种子集合并将它保存在seed中,然后重组选择器

select = Sizzle.select = function( selector, context, results, seed ) {
    var i, tokens, token, type, find,
        compiled = typeof selector === "function" && selector,
        match = !seed && tokenize( ( selector = compiled.selector || selector ) );

    results = results || [];

    // 如果只是单个选择器的情况,也即是没有逗号的情况:div, p,可以特殊优化一下
    if ( match.length === 1 ) {
        tokens = match[ 0 ] = match[ 0 ].slice( 0 );

        // 如果第一个是selector是id我们可以设置context快速查找
        if ( tokens.length > 2 && ( token = tokens[ 0 ] ).type === "ID" &&
            context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[ 1 ].type ] ) {

            context = ( Expr.find[ "ID" ]( token.matches[ 0 ]
                .replace( runescape, funescape ), context ) || [] )[ 0 ];
            if ( !context ) { // 如果context这个元素(selector第一个id选择器)都不存在就不用查找了
                return results;
            } else if ( compiled ) {
                context = context.parentNode;
            }

            // 去掉第一个id选择器
            selector = selector.slice( tokens.shift().value.length );
        }

        // 即是表示如果没有一些结构伪类,这些是需要用另一种方式过滤,在之后文章再详细剖析
        // 那么就从最后一条规则开始,先找出seed集合
        i = matchExpr[ "needsContext" ].test( selector ) ? 0 : tokens.length;

        while ( i-- ) {
            token = tokens[ i ];

            // 如果遇到了关系选择器中止
            // > + ~ 空格
            if ( Expr.relative[ ( type = token.type ) ] ) {
                break;
            }

            // 是否为ID,CLASS,TAG中的一种
            if ( ( find = Expr.find[ type ] ) ) {

                // 搜索到符合条件的初始集合seed
                // find可以看成getElementsByTagName
                if ( ( seed = find(
                    token.matches[ 0 ].replace( runescape, funescape ),
                    rsibling.test( tokens[ 0 ].type ) && testContext( context.parentNode ) ||
                        context
                ) ) ) {
                    tokens.splice( i, 1 );
                    selector = seed.length && toSelector( tokens );

                    // 如果当前剩余的选择器为空,提前返回结果
                    if ( !selector ) {
                        push.apply( results, seed );
                        return results;
                    }

                    // 已经找到了符合条件的seed集合,不管前边是否还有其他Token,跳出去
                    break;
                }
            }
        }
    }

    // 暂时不知道有什么用,应该是进一步过滤seed集合的
    ( compiled || compile( selector, match ) )(
        seed,
        context,
        !documentIsHTML,
        results,
        !context || rsibling.test( selector ) && testContext( context.parentNode ) || context
    );
    return results;
};
上一篇 下一篇

猜你喜欢

热点阅读