美文网首页jQuery源码笔记.jpg
jQuery源码#2 Sizzle 词法解析

jQuery源码#2 Sizzle 词法解析

作者: 柠檬果然酸 | 来源:发表于2020-04-15 22:37 被阅读0次

    本节内容
    1.Sizzle.tokenize

    Sizzle引擎
    这是一个CSS选择器引擎,用于解析像div > p + .aaron[type="checkbox", display="none"], #id:first-child这样的CSS选择器。

    从右到左解析选择器
    因为这样效率更高(从右边查询一开始锁定的范围就很小)。

    词法分析
    js解析器把脚本代码的字符流转换成记号流,这个记号流就是Token序列

    Token: {  
       value: '匹配到的字符串', 
       type: '对应的Token类型', 
       matches: '正则匹配到的一个结构'
    }
    

    然后再用这个Token序列去做各种各样的事

    Sizzle.tokenize
    将字符串的CSS选择器解析成Token序列

    // 假设传入进来的选择器是:div > p + .aaron[type="checkbox"], #id:first-child
    // 解析器会以","为分隔符,将字符串分为两个部分解析
    // 返回Token序列
    tokenize = Sizzle.tokenize = function( selector, parseOnly ) {
    
        // soFar表示字符串未解析的部分
        // groups存放已经解析好的Token序列
        var matched, match, tokens, type,
            soFar, groups, preFilters,
            cached = tokenCache[ selector + " " ];
    
        if ( cached ) {
            return parseOnly ? 0 : cached.slice( 0 );
        }
    
        soFar = selector;
        groups = [];
        preFilters = Expr.preFilter;
    
        // 循环检测字符串
        while ( soFar ) {
    
            // 检查是否有","
            if ( !matched || ( match = rcomma.exec( soFar ) ) ) {
                if ( match ) { // 如果有就清除","
                    soFar = soFar.slice( match[ 0 ].length ) || soFar;
                }
    
                // 因为一开始matched == undefined,所以注定要往groups里面压入一个空数组(Token序列)
                groups.push( ( tokens = [] ) );
            }
    
            matched = false;
    
            // 处理特殊的Token:>, +, 空格, ~
            if ( ( match = rcombinators.exec( soFar ) ) ) {
                matched = match.shift();
                tokens.push( {
                    value: matched,
                    type: match[ 0 ].replace( rtrim, " " )
                } );
    
                // 处理完之后将其从待处理的字符中删除
                soFar = soFar.slice( matched.length );
            }
    
            // 这里开始分析这几种Token:TAG, ID, CLASS, ATTR, CHILD, PSEUDO, NAME
            // 如果通过正则匹配到了Token格式:match = matchExpr[ type ].exec( soFar )
            // 然后看看需不需要预处理:!preFilters[ type ]
            // 如果需要,那么通过预处理器将匹配到的处理一下:match = preFilters[ type ]( match )
            for ( type in Expr.filter ) {
                if ( ( match = matchExpr[ type ].exec( soFar ) ) && ( !preFilters[ type ] ||
                    ( match = preFilters[ type ]( match ) ) ) ) {
                    matched = match.shift();
                    tokens.push( {
                        value: matched,
                        type: type,
                        matches: match
                    } );
    
                    // 处理完之后将其从待处理的字符中删除
                    soFar = soFar.slice( matched.length );
                }
            }
    
            // 如果到了这里都还没matched到,那么说明这个选择器在这里有错误
            // 直接中断词法分析过程
            // 这就是Sizzle对词法分析的异常处理
            if ( !matched ) {
                break;
            }
        }
    
        // 如果只需要这个接口检查选择器的合法性,直接就返回soFar的剩余长度,因为soFar长度大于零说明选择器不合法
        // 其余情况,如果soFar不等于"",抛出异常;否则把groups记录在cache里边并返回
        return parseOnly ?
            soFar.length :
            soFar ?
                Sizzle.error( selector ) :
    
                // 放到tokenCache函数里进行缓存
                tokenCache( selector, groups ).slice( 0 );
    };
    

    通过注释,这个方法的结构变得更简单了。最难理解的地方是matchExpr里的正则表达式,列几个出现较多的正则表达式

    空白符
    whitespace = "[\\x20\\t\\r\\n\\f]"

    符号 意义
    \x20 空格
    \t 制表符
    \r 回车
    \n 换行
    \f 分页符

    matchExpr['CLASS']
    /^\.((?:\\[\da-fA-F]{1,6}[\x20\t\r\n\f]?|\\[^\r\n\f]|[\w-]|[^-\x7f])+)/
    匹配className的正则表达式,将其拆分成三个部分

    \\[\da-fA-F]{1,6}[\x20\t\r\n\f]?
    后面部分又出现了匹配空白符的正则表达式

    \\[^\r\n\f]
    除开回车、换行、分页的所有字符

    [\w-]
    \w是a-zA-Z0-9_的简写

    [^-\x7f]
    ASCII码中没查到\x7f,所以作用也不得而知·

    相关文章

      网友评论

        本文标题:jQuery源码#2 Sizzle 词法解析

        本文链接:https://www.haomeiwen.com/subject/tvrfoctx.html