亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

jQuery選擇器源碼解讀(五):tokenize的解析過程

2019-11-20 12:49:18
字體:
來源:轉載
供稿:網友

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

下面將以$("div:not(.class:contain('span')):eq(3)")為例,說明tokenize和preFilter各段代碼是如何協調完成解析的。若想了解tokenize方法和preFilter類的每行代碼的詳細解釋,請參看如下兩篇文章:

//www.49028c.com/article/63155.htm
//www.49028c.com/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選擇器,故結束本次循環。
第二次for循環:從Expr.filter中取出第二個元素"CLASS"賦給type變量,執行循環體代碼。

match = matchExpr[type].exec(soFar)的執行結果如下:

match = ["class" , "class"]

由于不存在preFilters["CLASS"],故執行if內語句體。

復制代碼 代碼如下:

matched = match.shift(); 

 移除match中的第一個元素class,并將該元素賦予matched變量,此時matched="class",match = ["class"]

復制代碼 代碼如下:

tokens.push({ 
    value : matched, 
    type : type, 
    matches : match 

創建一個新對象{ value: "class", type:"CLASS", matches: ["class"] },并將該對象壓入tokens數組。

復制代碼 代碼如下:

soFar = soFar.slice(matched.length); 

soFar變量刪除class,此時,soFar = ":contain('span')):eq(3"
第三次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)的執行結果如下:
[":contain('span')", "contain", "'span'", "'", "span", 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參數等于:
[":contain('span')", "contain", "'span'", "'", "span", undefined, undefined, undefined, undefined, undefined, undefined]

復制代碼 代碼如下:

unquoted = !match[5] && match[2]; 

unquoted = "span"

復制代碼 代碼如下:

 if (matchExpr["CHILD"].test(match[0])) {
  return null;
 }

由于":contain('span')"不匹配matchExpr["CHILD"]正則表達式,故不執行內部語句體。

復制代碼 代碼如下:

 if (match[3] && match[4] !== undefined) {
  match[2] = match[4];
 }

 由于match[3] = "'",match[4] ="span",故執行if內部語句體,將"span"賦予match[2]

復制代碼 代碼如下:

return match.slice(0, 3); 

返回match前三個元素的副本
此時回到tokenize方法的for循環內繼續執行,此時各變量值如下:

match = [":contain('span')", "contain", "span"]

soFar = ":contain('span')):eq(3"

復制代碼 代碼如下:

matched = match.shift(); 

 將":contain('span')"移除match數組,并賦予matched變量

復制代碼 代碼如下:

tokens.push({ 
    value : matched, 
    type : type, 
    matches : match 


 創建一個新對象{ value:
":contain('span')", type:"PSEUDO", matches: ["contain", "span"] },并將該對象壓入tokens數組。

復制代碼 代碼如下:

soFar = soFar.slice(matched.length); 

soFar變量刪除":contain('span')",此時,soFar="):eq(3)",之后,直至for循環結束,且再次執行while循環,也沒有一個有效選擇器,故退出while循環。

復制代碼 代碼如下:

return parseOnly ? soFar.length : soFar ? Sizzle.error(selector) : 
    tokenCache(selector, groups).slice(0); 

 由于此時parseOnly = true,故返回此時soFar的長度6,繼續執行preFilters["PSEUDO"]的代碼

 

復制代碼 代碼如下:

 else if (unquoted 
        && rpseudo.test(unquoted) 
        && (excess = tokenize(unquoted, true)) 
        && (excess = unquoted.indexOf(")", unquoted.length - excess) - unquoted.length) 
 

 將6賦予excess變量,然后由代碼
 

復制代碼 代碼如下:

 excess = unquoted.indexOf(")", unquoted.length - excess) - unquoted.length 
 

 計算出:not選擇器結束位置(即右括號位置)22

復制代碼 代碼如下:

match[0] = match[0].slice(0, excess); 
match[2] = unquoted.slice(0, excess); 

分別計算出完整的:not選擇器字符串(match[0])和其括號內的字符串(match[2]),分別等于:

match[0] = ":not(.class:contain('span'))"

match[2] = ".class:contain('span')"

復制代碼 代碼如下:

return match.slice(0, 3);

返回match中前三個元素的副本。
回到tokenize函數,此時match = [":not(.class:contain('span'))", "not", ".class:contain('span')"]

復制代碼 代碼如下:

matched = match.shift();

移除match中的第一個元素":not(.class:contain('span'))",并將該元素賦予matched變量,此時matched="":not(.class:contain('span'))"",
match = ["not", ".class:contain('span')"]

復制代碼 代碼如下:

tokens.push({ 
    value : matched, 
    type : type, 
    matches : match 

 創建一個新對象{ value: ":not(.class:contain('span'))"", type:"PSEUDO", matches:  ["not", ".class:contain('span')"]  },并將該對象壓入tokens數組。此時tokens共有兩個元素分別是div和not選擇器。

復制代碼 代碼如下:

soFar = soFar.slice(matched.length); 

 soFar變量刪除":not(.class:contain('span'))",此時,soFar=":eq(3)",結束本次for循環后,再次回到while循環,同樣方式,獲取tokens的第三個元素eq選擇器,過程與not一致,這里就不再細講了。最后的groups的結果如下:
group[0][0] = {value: "div", type: "TAG", matches: ["div"]  }

group[0][1] = {value: ":not(.class:contain('span'))", type: "PSEUDO", matches: ["not", ".class:contain('span')"] }

group[0][2] = {value: ":eq(3)", type: "PSEUDO", matches: ["eq", "3"] }

復制代碼 代碼如下:

return parseOnly ? soFar.length : soFar ? Sizzle.error(selector) : 
    tokenCache(selector, groups).slice(0); 

由于parseOnly = undefined,所以執行tokenCache(selector, groups).slice(0),該語句將groups壓入緩存,并返回其副本。
由此,完成了所有的解析,或許有人會問,這里第二個元素并沒有解析出來呀,是的,這個需要在實際運行中再次解析。當然,這里若可以將剛才解析."class:contain('span')):eq(3"時,將有效選擇器的結果保存到緩存內,那么就可以避免再次解析,提高執行速度。但這也僅僅提高了當前這次運行速度。因為在執行過程中,對".class:contain('span')"再次提交解析時,會存入緩存。

至此,整個執行過程已經全部結束。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
黑人与娇小精品av专区| 亚洲xxxxx| 亚洲欧美一区二区三区情侣bbw| 综合欧美国产视频二区| 亚洲成av人乱码色午夜| 17婷婷久久www| 精品福利樱桃av导航| 亚洲视频一区二区| 国产性猛交xxxx免费看久久| 国内外成人免费激情在线视频| 91福利视频在线观看| 国自产精品手机在线观看视频| 美乳少妇欧美精品| 欧美亚洲国产视频| 久久久久久午夜| 国产精品久久久久久久久久久久| 亚洲第一男人av| 亚洲美女激情视频| 91精品中文在线| 日韩欧美在线免费| 另类图片亚洲另类| 都市激情亚洲色图| 中文字幕不卡在线视频极品| 欧美猛交免费看| 精品久久久久久久久久久久久久| 成人免费在线视频网址| 欧美午夜宅男影院在线观看| 国产成人精品网站| 日韩黄色在线免费观看| 欧美成人久久久| 最近中文字幕mv在线一区二区三区四区| 欧美在线影院在线视频| 国产一区二区视频在线观看| 亚洲福利视频免费观看| 日本韩国在线不卡| 成人在线中文字幕| 性视频1819p久久| 久久久久久一区二区三区| 亚洲欧美精品一区二区| www国产91| 亚洲free性xxxx护士hd| 91久久国产综合久久91精品网站| 久久国产精品影片| 色悠久久久久综合先锋影音下载| 亚洲最大的成人网| 91在线精品播放| 国产精品久久久久影院日本| 亚洲国产精品久久久久秋霞蜜臀| 日韩精品中文字幕在线观看| 91精品久久久久久久久久| 日韩精品视频在线免费观看| 国产精品91在线| 伊人激情综合网| 九九综合九九综合| 亚洲美女中文字幕| 91精品国产综合久久香蕉922| 国产亚洲视频在线观看| 亚洲人成在线观看网站高清| 欧美成人激情视频| 国产精品视频yy9099| 久久久国产精品免费| 国产精品视频区| 欧美日韩中文字幕日韩欧美| 欧美美女18p| 欧美裸体男粗大视频在线观看| 国产精品自在线| 日韩中文综合网| 国产精彩精品视频| 久久久久久久久久久亚洲| 亚洲一区制服诱惑| 欧美成aaa人片在线观看蜜臀| 亚洲欧洲偷拍精品| 成人久久18免费网站图片| 国产小视频国产精品| 亚洲国产精品久久| 成人在线免费观看视视频| 欧美精品久久久久久久久久| 国产精品极品美女粉嫩高清在线| 91九色单男在线观看| 国产精品久久久| 亚洲美女视频网| 欧美在线一区二区视频| 在线观看日韩www视频免费| 日韩不卡中文字幕| 欧美伊久线香蕉线新在线| 国产精品久久久久av| 午夜精品理论片| 午夜精品久久久久久99热软件| 色先锋久久影院av| 欧美日本啪啪无遮挡网站| 亚洲新中文字幕| 国产精品久久久久久久久久久久久久| 欧美在线视频a| 欧美电影在线观看| 日韩电视剧免费观看网站| 亚洲欧美成人一区二区在线电影| 2018国产精品视频| 亚洲国产精品va在线| 欧美午夜xxx| 97久久精品在线| 正在播放国产一区| 欧美性极品xxxx娇小| 欧美插天视频在线播放| 亚洲精品自拍视频| 久久久久久国产精品久久| 国产suv精品一区二区三区88区| 国产亚洲欧美视频| 日韩av电影中文字幕| 久久综合久中文字幕青草| 91国内产香蕉| 亚洲国产高清福利视频| 国产一区二区香蕉| 欧美猛交ⅹxxx乱大交视频| 欧美性猛交丰臀xxxxx网站| 成人女保姆的销魂服务| 亚洲男人av电影| 1769国内精品视频在线播放| 欧美中文字幕在线视频| 久久亚洲欧美日韩精品专区| 精品国产一区av| 久久成人人人人精品欧| 国产啪精品视频| 欧美丰满少妇xxxx| 欧美色视频日本高清在线观看| 国产视频自拍一区| 亚洲精品小视频| 欧美性一区二区三区| 国模精品视频一区二区| www.亚洲一二| 欧美日韩精品二区| 亚洲一区二区三区sesese| 中文字幕欧美精品日韩中文字幕| 91sao在线观看国产| 日韩视频在线观看免费| 国产精品伦子伦免费视频| 欧美日韩激情美女| 午夜精品www| 色伦专区97中文字幕| 国产成人高清激情视频在线观看| 视频一区视频二区国产精品| 国产91在线播放九色快色| 九九九久久国产免费| 亚洲综合在线播放| 精品亚洲一区二区三区在线观看| 亚洲一区av在线播放| 国模视频一区二区| 亚洲欧美日韩一区二区在线| 国产精品高潮在线| 午夜精品三级视频福利| 96精品久久久久中文字幕| 在线亚洲午夜片av大片| 92看片淫黄大片欧美看国产片| 国产精品青草久久久久福利99| 中文字幕精品在线视频| 欧美成人精品一区| 国产精品va在线播放我和闺蜜| 91精品国产成人www| 成人欧美在线观看| 亚洲日本中文字幕| 欧美一二三视频| 中文在线资源观看视频网站免费不卡| 亚洲高清免费观看高清完整版| 亚洲国产成人久久综合一区| 国产不卡一区二区在线播放|