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

首頁 > 語言 > JavaScript > 正文

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

2024-05-06 16:17:56
字體:
來源:轉載
供稿:網友
這篇文章主要介紹了jQuery選擇器源碼解讀(五):tokenize的解析過程,本文用詳細的注釋解讀了tokenize方法的解析過程,需要的朋友可以參考下
 

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

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

http://www.49028c.com/kaifa/javascript/49411.html

 http://www.49028c.com/kaifa/javascript/49448.html

下面是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
亚洲最新在线视频| 这里只有精品在线播放| 成人啪啪免费看| 国产精品福利在线| 美女999久久久精品视频| 欧美亚洲第一区| 成人av在线网址| 亲爱的老师9免费观看全集电视剧| 久久视频精品在线| 久久国产精品影视| 亚洲毛片在线免费观看| 国产福利视频一区二区| 久久国产精品亚洲| 成人免费福利在线| 伊人伊成久久人综合网站| 91精品视频免费看| 欧美激情在线有限公司| 国产精品视频免费观看www| 深夜成人在线观看| 国产一区二区三区精品久久久| 韩日精品中文字幕| 日韩精品中文字幕有码专区| 国产一区二区丝袜高跟鞋图片| 中文字幕在线精品| 国产成人精品视频在线观看| 亚洲人成在线播放| 国产精品福利在线观看网址| 91在线直播亚洲| 亚洲自拍偷拍福利| 国产极品精品在线观看| 亚洲成人中文字幕| 久久精品久久久久电影| 91在线观看免费观看| www.欧美三级电影.com| 亚洲欧美色图片| 欧美成人在线网站| 亚洲欧美日韩视频一区| 91系列在线播放| 日本三级韩国三级久久| 午夜精品美女自拍福到在线| 欧美亚洲国产视频| 欧美精品手机在线| 欧美性xxxxxxx| 欧洲日韩成人av| 欧美在线不卡区| 日韩在线www| 亚洲视屏在线播放| 国产精品老女人精品视频| 久久精品国产成人| 亚洲美女av在线播放| 亚洲xxx自由成熟| 久久99热这里只有精品国产| 日韩av毛片网| 国产一区二区日韩精品欧美精品| 日本aⅴ大伊香蕉精品视频| 亚洲欧美福利视频| 日韩在线观看高清| 国产欧美一区二区三区久久人妖| 国产极品jizzhd欧美| 91精品国产乱码久久久久久蜜臀| 国产精品一久久香蕉国产线看观看| 91探花福利精品国产自产在线| 欧美猛少妇色xxxxx| 久久久久成人精品| 国语自产精品视频在线看抢先版图片| 中文字幕免费精品一区| 久久久亚洲成人| 欧美精品精品精品精品免费| 亚洲精品国产品国语在线| 亚洲高清不卡av| 欧美激情综合色综合啪啪五月| 日韩在线观看免费全集电视剧网站| 亚洲男人天堂视频| 日韩大陆毛片av| 久久久精品视频成人| 国产做受69高潮| 日韩视频免费在线观看| 在线激情影院一区| 色先锋久久影院av| 国产一区二区av| 国产精品自拍偷拍| 91精品国产高清久久久久久久久| 亚洲国产欧美在线成人app| xxxxx91麻豆| 在线精品国产成人综合| 搡老女人一区二区三区视频tv| 亚洲精品美女久久久久| 国产成人精品a视频一区www| 亚洲美女av网站| 欧美在线视频在线播放完整版免费观看| 日韩精品视频中文在线观看| 国产精品成人av性教育| 亚洲欧美日韩精品| 欧美综合一区第一页| 日韩欧美亚洲国产一区| 国产九九精品视频| 欧美一级视频免费在线观看| 日韩精品欧美国产精品忘忧草| 日韩av三级在线观看| 91中文字幕在线观看| 亚洲区bt下载| 久久久久久久爱| 深夜福利日韩在线看| 欧美黄色片免费观看| 国内成人精品一区| 狠狠色狠狠色综合日日五| 国产一级揄自揄精品视频| 亚洲资源在线看| 日日摸夜夜添一区| 国产精品国产三级国产aⅴ浪潮| 国产精品青青在线观看爽香蕉| 91精品久久久久久久久久另类| www.日本久久久久com.| 91精品视频免费看| 国产不卡一区二区在线播放| 国产精品www色诱视频| 欧美专区第一页| 久久精品国产亚洲精品| 国产美女被下药99| 亚洲欧美国产制服动漫| 成人中文字幕+乱码+中文字幕| 久久亚洲欧美日韩精品专区| 日韩视频在线免费| 国产精品美女免费视频| 国产欧美精品日韩| 奇米成人av国产一区二区三区| 国产精品男人爽免费视频1| 日韩精品免费在线观看| 欧美日韩在线免费观看| 久久久久国色av免费观看性色| 亚洲2020天天堂在线观看| 日本19禁啪啪免费观看www| 国产精品揄拍一区二区| 国产精品一区二区久久久久| 亚洲精品国产suv| 久久久国产精品免费| 久久精品成人一区二区三区| 精品福利免费观看| 国内成人精品一区| 亚洲字幕一区二区| 欧美裸体男粗大视频在线观看| 亚洲天堂免费视频| 狠狠色狠狠色综合日日小说| 日韩欧美国产网站| 欧美日韩一区二区免费视频| 欧美裸体男粗大视频在线观看| 美日韩精品免费观看视频| 91精品久久久久久久久久| 中文字幕在线成人| 成人av在线网址| 69影院欧美专区视频| 国产美女久久久| 国产亚洲视频在线观看| 国产日韩欧美在线观看| 日韩精品一区二区三区第95| 欧美色另类天堂2015| 久久人人97超碰精品888| 亚洲视频在线播放| 国产精品av免费在线观看| 国产99视频在线观看| 蜜臀久久99精品久久久无需会员| 国产精品九九久久久久久久| 三级精品视频久久久久| 国产在线视频2019最新视频|