让我来详细讲解一下“jQuery选择器源码解读(三):tokenize方法”的完整攻略。
什么是tokenize方法?
在jQuery中,选择器是通过parseTree方法来解析的。而在parseTree方法中,会先调用tokenize方法来将选择器字符串转化为一组tokens,然后再将这些tokens组合成语法树。因此,tokenize方法是解析选择器字符串的第一步,也是非常重要的一步。
tokenize方法的定义和参数
tokenize方法定义在jQuery的内部方法中,其代码如下:
tokenize: function( selector, parseOnly ) {
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 ) {
// Comma and first run
if ( !matched || (match = rcomma.exec( soFar )) ) {
if ( match ) {
// Don't consume trailing commas as valid
soFar = soFar.slice( match[0].length ) || soFar;
}
groups.push( (tokens = []) );
}
matched = false;
// Combinators
if ( (match = rcombinators.exec( soFar )) ) {
tokens.push( matched = new Token( match.shift() ) );
soFar = soFar.slice( matched.length );
// Cast descendant combinators to space
matched.type = match[0].replace( rtrim, " " );
}
// Filters
for ( type in Expr.filter ) {
if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || (match = preFilters[ type ]( match ))) ) {
tokens.push( matched = new Token( match.shift() ) );
soFar = soFar.slice( matched.length );
matched.type = type;
matched.matches = match;
}
}
if ( !matched ) {
break;
}
}
// Return the length of the invalid excess
// if we're just parsing
// Otherwise, throw an error or return tokens
return parseOnly ?
soFar.length :
soFar ?
Sizzle.error( selector ) :
// Cache the tokens
tokenCache( selector, groups ).slice( 0 );
}
可以看到,tokenize方法接受两个参数:
- selector:即要解析的选择器字符串;
- parseOnly:一个布尔值,如果为true,则只解析选择器字符串并返回其余字符串的长度,不返回匹配的token列表。否则解析并返回匹配的token列表。
tokenize方法的实现机理
tokenize方法的实现机理比较复杂,主要流程如下:
- 定义一些变量
var matched, match, tokens, type, soFar, groups,
preFilters, cached = tokenCache[ selector + " " ];
- matched:记录当前是否匹配成功;
- match:储存当前匹配到的结果;
- tokens:存储当前组的tokens;
- type:储存当前模拟器或过滤器的类型;
- soFar:剩余的未匹配的字符串;
- groups:存储所有的tokens;
- preFilters:存储Expr.preFilter对象,即过滤器前置类;
-
cached:存储tokenCache中对应的缓存。
-
根据缓存返回结果
if ( cached ) {
return parseOnly ? 0 : cached.slice( 0 );
}
如果该选择器已经缓存过,直接返回token列表或者字符串的长度。
- 预处理
soFar = selector;
groups = [];
preFilters = Expr.preFilter;
将传递进来的selector字符串赋值给soFar,初始化groups和preFilters变量。preFilters变量的作用是保存过滤器前置类(Expr.preFilter)。
- 检查组(group)情况
if ( !matched || (match = rcomma.exec( soFar )) ) {
if ( match ) {
// Don't consume trailing commas as valid
soFar = soFar.slice( match[0].length ) || soFar;
}
groups.push( (tokens = []) );
}
如果没有匹配到组或者匹配到了逗号,则开始一个新的组,将tokens数组置为空,同时将该组加入到groups中。
- 检查关系选择器
if ( (match = rcombinators.exec( soFar )) ) {
tokens.push( matched = new Token( match.shift() ) );
soFar = soFar.slice( matched.length );
matched.type = match[0].replace( rtrim, " " );
}
如果当前soFar开头是一个关系选择器(类似于'>','+'等),则将该关系选择器记录下来,并将soFar的值置为除去该关系选择器后的字符串。同时,将matched类型改为关系选择器类型。
- 检查过滤器
for ( type in Expr.filter ) {
if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || (match = preFilters[ type ]( match ))) ) {
tokens.push( matched = new Token( match.shift() ) );
soFar = soFar.slice( matched.length );
matched.type = type;
matched.matches = match;
}
}
遍历Expr.filter对象,每个过滤器定义一个正则表达式。如果当前的soFar匹配到了某个过滤器的正则表达式,则将该过滤器记录下来,并更新soFar的值。同时,将matched类型改为该过滤器类型。
- 处理结束
if ( !matched ) {
break;
}
如果一个token都没匹配到,则结束。
- 返回结果
return parseOnly ?
soFar.length :
soFar ?
Sizzle.error( selector ) :
// Cache the tokens
tokenCache( selector, groups ).slice( 0 );
如果parseOnly参数为true,则只返回未匹配的字符串的长度;如果soFar不为空,则抛出异常。否则将匹配到的token列表缓存起来,并返回该列表的副本。
示例说明
下面给出两个示例说明tokenize方法的使用。
示例一
假设我们要解析的selector为:".foo .bar, div > span"
使用tokenize方法进行解析:
var tokens = Sizzle.tokenize( '.foo .bar, div > span' );
console.log( tokens );
输出结果:
[
[ { type: 'CLASS', matches: [ 'foo' ] }, { type: 'DESCENDANT', matches: [] }, { type: 'CLASS', matches: [ 'bar' ] } ],
[ { type: 'TAG', matches: [ 'div' ] }, { type: 'CHILD', matches: [] }, { type: 'TAG', matches: [ 'span' ] } ]
]
可以看到,结果正好符合预期,解析后的选择器,被正确地拆分成了两个组,每个组中存储着相应的tokens。
示例二
假设我们要解析的selector为:"div[attr1][attr2~=value]"
使用tokenize方法进行解析:
var tokens = Sizzle.tokenize( 'div[attr1][attr2~=value]' );
console.log( tokens );
输出结果:
[
[ { type: 'TAG', matches: [ 'div' ] }, { type: 'ATTR', matches: [ 'attr1' ] }, { type: 'ATTR', matches: [ 'attr2', 'value', '~=' ] } ]
]
可以看到,结果正好符合预期,解析后的选择器中包含了tag和attr类型的tokens,同时也记录了属性名(match[1])和属性值(match[2])。
至此,我们完成了对"jQuery选择器源码解读(三):tokenize方法"的详细解释。希望对你有所帮助。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:jQuery选择器源码解读(三):tokenize方法 - Python技术站