以下分析基于jQuery-1.10.2.js版本。
下面将以$("div:not(.class:contain('span')):eq(3)")为例,说明tokenize和preFilter各段代码是如何协调完成解析的。若想了解tokenize方法和preFilter类的每行代码的详细解释,请参看如下两篇文章:
http://www.jb51.net/article/63155.htm
http://www.jb51.net/article/63163.htm
下面是tokenize方法的源码,为了简便期间,我把有关缓存、逗号的匹配以及关系符的匹配的代码全部去掉了,只留了与当前例子有关的核心代码。被去掉的代码很简单,若需要可以看一下上述文章即可。
另外,代码统一写在说明文字上方。
function tokenize(selector, parseOnly) {
var matched, match, tokens, type, soFar, groups, preFilters;
soFar = selector;
groups = [];
preFilters = Expr.preFilter;
while (soFar) {
if (!matched) {
groups.push(tokens = []);
}
matched = false;
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);
}
}
if (!matched) {
break;
}
}
return parseOnly ? soFar.length : soFar ? Sizzle.error(selector) :
tokenCache(selector, groups).slice(0);
}
首先,jQuery执行过程中由select方法首次调用tokenize,并将"div:not(.class:contain('span')):eq(3)"作为selector参数传入该方法。
soFar = selector;
soFar = "div:not(.class:contain('span')):eq(3)"
第一次进入while循环时,由于matched还未被赋值,所以执行if内的如下语句体,该语句将初始化tokens变量,同时,将tokens压入groups数组。
groups.push(tokens = []);
之后,进入for语句。
第一次for循环:从Expr.filter中取出第一个元素"TAG"赋给type变量,执行循环体代码。
if ((match = matchExpr[type].exec(soFar))
&& (!preFilters[type] || (match = preFilters[type]
(match)))) {
match = matchExpr[type].exec(soFar)的执行结果如下:
match =["div", "div"]
示例的第一个选择器为div,匹配matchExpr["TAG"]的正则表达式,且不存在preFilters["TAG"],故执行if内语句体。
matched = match.shift();
移除match中的第一个元素div,并将该元素赋予matched变量,此时matched="div",match = ["div"]
tokens.push({
value : matched,
type : type,
matches : match
}
创建一个新对象{ value: "div", type:"TAG", matches: ["div"] },并将该对象压入tokens数组。
soFar = soFar.slice(matched.length);
soFar变量删除div,此时,soFar=":not(.class:contain('span')):eq(3)"
第二次for循环:从Expr.filter中取出第二个元素"CLASS"赋给type变量,执行循环体代码。
if ((match = matchExpr[type].exec(soFar))
&& (!preFilters[type] || (match = preFilters[type]
(match)))) {
由于当前的soFar=":not(.class:contain('span')):eq(3)",不匹配CLASS类型的正则表达式,故结束本次循环。
第三次for循环:从Expr.filter中取出第三个元素"ATTR"赋给type变量,执行循环体代码。
同样,由于当前剩余选择器不是属性选择器,故结束本次循环。
第四次for循环:从Expr.filter中取出第四个元素"CHILD"赋给type变量,执行循环体代码。
同样,由于当前剩余选择器不是CHILD选择器,故结束本次循环。
第五次for循环:从Expr.filter中取出第五个元素"PSEUDO"赋给type变量,执行循环体代码。
if ((match = matchExpr[type].exec(soFar))
&& (!preFilters[type] || (match = preFilters[type]
(match)))) {
match = matchExpr[type].exec(soFar)的执行结果如下:
[":not(.class:contain('span')):eq(3)", "not", ".class:contain('span')):eq(3", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined]
由于存在preFilters["PSEUDO"],故执行其后的代码:
match = preFilters[type](match)
preFilters["PSEUDO"]代码如下:
"PSEUDO" : function(match) {
var excess, unquoted = !match[5] && match[2];
if (matchExpr["CHILD"].test(match[0])) {
return null;
}
if (match[3] && match[4] !== undefined) {
match[2] = match[4];
} else if (unquoted
&& rpseudo.test(unquoted)
&& (excess = tokenize(unquoted, true))
&& (excess = unquoted.indexOf(")", unquoted.length
- excess)
- unquoted.length)) {
match[0] = match[0].slice(0, excess);
match[2] = unquoted.slice(0, excess);
}
return match.slice(0, 3);
}
传入的match参数等于:
[":not(.class:contain('span')):eq(3)", "not", ".class:contain('span')):eq(3", undefined, undefined, undefined, undefined, undefined
unquoted = !match[5] && match[2]
unquoted = ".class:contain('span')):eq(3"
if (matchExpr["CHILD"].test(match[0])) {
return null;
}
match[0] = ":not(.class:contain('span')):eq(3)",不匹配matchExpr["CHILD"]正则表达式,不执行return null语句。
if (match[3] && match[4] !== undefined) {
match[2] = match[4];
}
由于match[3]和match[4]都等于undefined,故执行else的语句体。
else if (unquoted
&& rpseudo.test(unquoted)
&& (excess = tokenize(unquoted, true))
&& (excess = unquoted.indexOf(")", unquoted.length - excess) - unquoted.length)
此时,unquoted = ".class:contain('span')):eq(3",为真,而且由于unquoted含有:contain('span'),与正则表达式rpseudo匹配,故rpseudo.test(unquoted)为真,然后再次调用tokenize对unquoted再次解析,如下语句:
excess = tokenize(unquoted, true)
此次调用tokenize函数时,传入的selector参数等于".class:contain('span')):eq(3",parseOnly等于true。函数体内执行过程如下:
soFar = selector;
soFar = ".class:contain('span')):eq(3"
第一次进入while循环时,由于matched还未被赋值,所以执行if内的如下语句体,该语句将初始化tokens变量,同时,将tokens压入groups数组。
groups.push(tokens = []);
之后,进入for语句。
第一次for循环:从Expr.filter中取出第一个元素"TAG"赋给type变量,执行循环体代码。
if ((match = matchExpr[type].exec(soFar))
&& (!preFilters[type] || (match = preFilters[type]
(match)))) {
由于当前剩余选择器不是TAG选择器,故

