本节内容
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,所以作用也不得而知·
网友评论