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;
};