实例说明tokenize的解析过程

原创文章,转载请写明出处,多谢!

以下分析基于jQuery-1.10.2.js版本。

下面将以$("div:not(.class:contain(‘span’)):eq(3)")为例,说明tokenize和preFilter各段代码是如何协调完成解析的。若想了解tokenize方法和preFilter类的每行代码的详细解释,请参看如下两篇文章:

jQuery选择器代码详解(三)——tokenize方法

jQuery选择器代码详解(四)——Expr.preFilter

下面是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参数传入该方法。

<pre name="code" class="javascript">soFar = selector;soFar ="div:not(.class:contain(‘span’)):eq(3)"

第一次进入while循环时,由于matched还未被赋值,所以执行if内的如下语句体,该语句将初始化tokens变量,同时,将tokens压入groups数组。

<span style="white-space:pre"></span>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内语句体。

<span style="white-space:pre"></span>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"],故执行其后的代码:

<span style="white-space:pre"></span>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, undefined, undefined, undefined]

<span style="white-space:pre"></span>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的语句体。<span style="white-space:pre"></span>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再次解析,如下语句:<span style="white-space:pre"></span>excess = tokenize(unquoted, true)此次调用tokenize函数时,传入的selector参数等于".class:contain(‘span’)):eq(3",parseOnly等于true。函数体内执行过程如下:<span style="white-space:pre"></span>soFar = selector;soFar =".class:contain(‘span’)):eq(3"时间慢慢的流淌,人生有风雨阳光,

实例说明tokenize的解析过程

相关文章:

你感兴趣的文章:

标签云: