【jQuery源码】tokenize方法
1 //得到由选择器生成的token对象的数组(下面的groups) 2 //Sizzle的Token格式如下 :{value:'匹配到的字符串', type:'对应的Token类型', matches:'正则匹配到的一个结构'} 3 //比如"title,div > :nth-child(even)"解析下面的符号流 4 // [ [{value:"title",type:"TAG",matches:["title"]}], 5 // [{value:"div",type:["TAG",matches:["div"]}, 6 // {value:">", type: ">"}, 7 // {value:":nth-child(even)",type:"CHILD",matches:["nth", 8 // "child","even",2,0,undefined,undefined,undefined]} 9 // ] 10 // ] 11 //有多少个并联选择器,里面就有多少个数组,数组里面是拥有value与type的对象 12 13 //tokenize两个作用:1.解析选择器;2.将解析结果存入缓存 14 tokenize = Sizzle.tokenize = function( selector, parseOnly ) { 15 var matched, match, tokens, type, 16 soFar, groups, preFilters, 17 cached = tokenCache[ selector + " " ]; 18 //这里的soFar是表示目前还未分析的字符串剩余部分 19 20 //如果tokenCache中已经有selector了,则直接拿出来就好了 21 if ( cached ) { 22 return parseOnly ? 0 : cached.slice( 0 ); 23 } 24 25 soFar = selector; 26 groups = []; 27 28 //这里的预处理器为了对匹配到的Token适当做一些调整 29 preFilters = Expr.preFilter; 30 31 //循环处理字符串 32 while ( soFar ) { 33 34 // Comma and first run 35 //whitespace = "[\\x20\\t\\r\\n\\f]"; 36 //rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ); 37 //!matched:若是第一次执行循环体,则为true;否则为false。 38 //这里matched即作为是否第一次执行循环体的标识, 39 if ( !matched || (match = rcomma.exec( soFar )) ) { 40 if ( match ) { 41 // Don't consume trailing commas as valid 42 //去掉sofar中第一个的无用",",比如",div,#id"中的第一个"," 43 //举个例子,"div.news,span.closed" 44 //在解析过程中,首先由后续代码解析完毕div.news,剩下",span.closed" 45 //在循环体内执行到这里时,将逗号及之前之后连续的空白(match[0])删除掉, 46 //使soFar变成"span.closed",继续执行解析过程 47 soFar = soFar.slice( match[0].length ) || soFar; 48 } 49 //往规则组里边压入一个Token序列,目前Token序列还是空的 50 groups.push( (tokens = []) ); 51 } 52 53 matched = false; 54 55 // Combinators 56 //rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*"), 57 //rcombinators用来匹配四种关系符,即>+~和空白 58 if ( (match = rcombinators.exec( soFar )) ) { 59 // 举个例子: 60 // 若soFar = " + .div"; 61 // 执行match = rcombinators.exec(soFar)后, 62 // match[0] = " + ",而match[1]="+"; 63 // 执行完matched = match.shift()后, 64 // matched=" + ",而match[0]="+"; 65 matched = match.shift(); 66 tokens.push({ 67 value: matched, 68 // Cast descendant combinators to space 69 type: match[0].replace( rtrim, " " ) 70 }); 71 soFar = soFar.slice( matched.length ); 72 } 73 74 // Filters 75 //下面通过for语句对soFar逐一匹配ID、TAG、CLASS、CHILD、ATTR、PSEUDO类型的选择器 76 // 若匹配到了,则先调用该类型选择器对应的预过滤函数, 77 // 然后,将结果压入tokens数组,继续本次循环。 78 for ( type in Expr.filter ) { 79 // match = matchExpr[type].exec(soFar):对soFar调用type类型的正则表达式对soFar进行匹配 80 // 并将匹配结果赋予match。若未匹配到数据,则match为null。 81 // !preFilters[type]:若不存在type类型的预过滤函数,则为true 82 // match = preFilters[type](match):执行预过滤,并将结果返回给match 83 if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || 84 (match = preFilters[ type ]( match ))) ) { 85 // 将match[0]移除match数组,同时将它赋予matched 86 matched = match.shift(); 87 // 将匹配结果压入tokens数组中 88 tokens.push({ 89 value: matched, 90 type: type, 91 matches: match 92 }); 93 // 将匹配结果之后的字符串赋予soFar,继续解析 94 soFar = soFar.slice( matched.length ); 95 } 96 } 97 98 //matched为false,说明本次循环没有效的选择器(包括关系符和id、class等类型选择器) 99 if ( !matched ) { 100 break; 101 } 102 } 103 104 // Return the length of the invalid excess 105 // if we're just parsing 106 // Otherwise, throw an error or return tokens 107 return parseOnly ? 108 soFar.length : 109 soFar ? 110 Sizzle.error( selector ) : 111 // Cache the tokens 112 tokenCache( selector, groups ).slice( 0 ); 113 };
画一张直观图便于理解