jQuery选择器源码解读(五):tokenize的解析过程

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

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

https://www.jb51.net/article/63155.htm
https://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变量,执行循环体代码。
同样,由于当前剩余选择器不是属性选择器,故结束本次循环。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wgjpwf.html