最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

 更新時(shí)間:2015年03月31日 10:57:21   投稿:junjie  
這篇文章主要介紹了jQuery選擇器源碼解讀(五):tokenize的解析過(guò)程,本文用詳細(xì)的注釋解讀了tokenize方法的解析過(guò)程,需要的朋友可以參考下

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

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

http://m.fzitv.net/article/63155.htm
http://m.fzitv.net/article/63163.htm

下面是tokenize方法的源碼,為了簡(jiǎn)便期間,我把有關(guān)緩存、逗號(hào)的匹配以及關(guān)系符的匹配的代碼全部去掉了,只留了與當(dāng)前例子有關(guān)的核心代碼。被去掉的代碼很簡(jiǎn)單,若需要可以看一下上述文章即可。

另外,代碼統(tǒng)一寫在說(shuō)明文字上方。

復(fù)制代碼 代碼如下:

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執(zhí)行過(guò)程中由select方法首次調(diào)用tokenize,并將"div:not(.class:contain('span')):eq(3)"作為selector參數(shù)傳入該方法。
復(fù)制代碼 代碼如下:

 soFar = selector;

soFar = "div:not(.class:contain('span')):eq(3)"
第一次進(jìn)入while循環(huán)時(shí),由于matched還未被賦值,所以執(zhí)行if內(nèi)的如下語(yǔ)句體,該語(yǔ)句將初始化tokens變量,同時(shí),將tokens壓入groups數(shù)組。

復(fù)制代碼 代碼如下:

groups.push(tokens = []); 

之后,進(jìn)入for語(yǔ)句。

第一次for循環(huán):從Expr.filter中取出第一個(gè)元素"TAG"賦給type變量,執(zhí)行循環(huán)體代碼。

復(fù)制代碼 代碼如下:

   if ((match = matchExpr[type].exec(soFar))
     && (!preFilters[type] || (match = preFilters[type]
       (match)))) {

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

match =["div", "div"]

示例的第一個(gè)選擇器為div,匹配matchExpr["TAG"]的正則表達(dá)式,且不存在preFilters["TAG"],故執(zhí)行if內(nèi)語(yǔ)句體。

復(fù)制代碼 代碼如下:

matched = match.shift(); 

移除match中的第一個(gè)元素div,并將該元素賦予matched變量,此時(shí)matched="div",match = ["div"]

復(fù)制代碼 代碼如下:

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

創(chuàng)建一個(gè)新對(duì)象{ value: "div", type:"TAG", matches: ["div"] },并將該對(duì)象壓入tokens數(shù)組。

復(fù)制代碼 代碼如下:

    soFar = soFar.slice(matched.length);

soFar變量刪除div,此時(shí),soFar=":not(.class:contain('span')):eq(3)"
第二次for循環(huán):從Expr.filter中取出第二個(gè)元素"CLASS"賦給type變量,執(zhí)行循環(huán)體代碼。

復(fù)制代碼 代碼如下:

   if ((match = matchExpr[type].exec(soFar))
     && (!preFilters[type] || (match = preFilters[type]
       (match)))) {

由于當(dāng)前的soFar=":not(.class:contain('span')):eq(3)",不匹配CLASS類型的正則表達(dá)式,故結(jié)束本次循環(huán)。
第三次for循環(huán):從Expr.filter中取出第三個(gè)元素"ATTR"賦給type變量,執(zhí)行循環(huán)體代碼。
同樣,由于當(dāng)前剩余選擇器不是屬性選擇器,故結(jié)束本次循環(huán)。

第四次for循環(huán):從Expr.filter中取出第四個(gè)元素"CHILD"賦給type變量,執(zhí)行循環(huán)體代碼。
同樣,由于當(dāng)前剩余選擇器不是CHILD選擇器,故結(jié)束本次循環(huán)。

第五次for循環(huán):從Expr.filter中取出第五個(gè)元素"PSEUDO"賦給type變量,執(zhí)行循環(huán)體代碼。

復(fù)制代碼 代碼如下:

   if ((match = matchExpr[type].exec(soFar))
     && (!preFilters[type] || (match = preFilters[type]
       (match)))) {

match = matchExpr[type].exec(soFar)的執(zhí)行結(jié)果如下:
[":not(.class:contain('span')):eq(3)", "not", ".class:contain('span')):eq(3", undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined]

由于存在preFilters["PSEUDO"],故執(zhí)行其后的代碼:

復(fù)制代碼 代碼如下:

match = preFilters[type](match) 

preFilters["PSEUDO"]代碼如下:

復(fù)制代碼 代碼如下:

"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參數(shù)等于:

復(fù)制代碼 代碼如下:

[":not(.class:contain('span')):eq(3)", "not", ".class:contain('span')):eq(3", undefined, undefined, undefined, undefined, undefined

復(fù)制代碼 代碼如下:

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

unquoted = ".class:contain('span')):eq(3"

復(fù)制代碼 代碼如下:

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

 match[0] = ":not(.class:contain('span')):eq(3)",不匹配matchExpr["CHILD"]正則表達(dá)式,不執(zhí)行return null語(yǔ)句。

復(fù)制代碼 代碼如下:

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

由于match[3]和match[4]都等于undefined,故執(zhí)行else的語(yǔ)句體。

復(fù)制代碼 代碼如下:

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

 此時(shí),unquoted = ".class:contain('span')):eq(3",為真,而且由于unquoted含有:contain('span'),與正則表達(dá)式rpseudo匹配,故rpseudo.test(unquoted)為真,然后再次調(diào)用tokenize對(duì)unquoted再次解析,如下語(yǔ)句:

復(fù)制代碼 代碼如下:

excess = tokenize(unquoted, true) 

此次調(diào)用tokenize函數(shù)時(shí),傳入的selector參數(shù)等于".class:contain('span')):eq(3",parseOnly等于true。函數(shù)體內(nèi)執(zhí)行過(guò)程如下:

復(fù)制代碼 代碼如下:

soFar = selector; 

 soFar = ".class:contain('span')):eq(3"
第一次進(jìn)入while循環(huán)時(shí),由于matched還未被賦值,所以執(zhí)行if內(nèi)的如下語(yǔ)句體,該語(yǔ)句將初始化tokens變量,同時(shí),將tokens壓入groups數(shù)組。

復(fù)制代碼 代碼如下:

groups.push(tokens = []); 

之后,進(jìn)入for語(yǔ)句。

第一次for循環(huán):從Expr.filter中取出第一個(gè)元素"TAG"賦給type變量,執(zhí)行循環(huán)體代碼。

復(fù)制代碼 代碼如下:

if ((match = matchExpr[type].exec(soFar)) 
        && (!preFilters[type] || (match = preFilters[type] 
                (match)))) { 

由于當(dāng)前剩余選擇器不是TAG選擇器,故結(jié)束本次循環(huán)。
第二次for循環(huán):從Expr.filter中取出第二個(gè)元素"CLASS"賦給type變量,執(zhí)行循環(huán)體代碼。

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

match = ["class" , "class"]

由于不存在preFilters["CLASS"],故執(zhí)行if內(nèi)語(yǔ)句體。

復(fù)制代碼 代碼如下:

matched = match.shift(); 

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

復(fù)制代碼 代碼如下:

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

創(chuàng)建一個(gè)新對(duì)象{ value: "class", type:"CLASS", matches: ["class"] },并將該對(duì)象壓入tokens數(shù)組。

復(fù)制代碼 代碼如下:

soFar = soFar.slice(matched.length); 

soFar變量刪除class,此時(shí),soFar = ":contain('span')):eq(3"
第三次for循環(huán):從Expr.filter中取出第三個(gè)元素"ATTR"賦給type變量,執(zhí)行循環(huán)體代碼。
同樣,由于當(dāng)前剩余選擇器不是屬性選擇器,故結(jié)束本次循環(huán)。

第四次for循環(huán):從Expr.filter中取出第四個(gè)元素"CHILD"賦給type變量,執(zhí)行循環(huán)體代碼。
同樣,由于當(dāng)前剩余選擇器不是CHILD選擇器,故結(jié)束本次循環(huán)。

第五次for循環(huán):從Expr.filter中取出第五個(gè)元素"PSEUDO"賦給type變量,執(zhí)行循環(huán)體代碼。

復(fù)制代碼 代碼如下:

if ((match = matchExpr[type].exec(soFar)) 
        && (!preFilters[type] || (match = preFilters[type] 
                (match)))) { 

 match = matchExpr[type].exec(soFar)的執(zhí)行結(jié)果如下:
[":contain('span')", "contain", "'span'", "'", "span", undefined, undefined, undefined, undefined, undefined, undefined]

由于存在preFilters["PSEUDO"],故執(zhí)行其后的代碼:

復(fù)制代碼 代碼如下:

match = preFilters[type](match)

 preFilters["PSEUDO"]代碼如上所示,此處不再列舉。

復(fù)制代碼 代碼如下:

"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參數(shù)等于:
[":contain('span')", "contain", "'span'", "'", "span", undefined, undefined, undefined, undefined, undefined, undefined]

復(fù)制代碼 代碼如下:

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

unquoted = "span"

復(fù)制代碼 代碼如下:

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

由于":contain('span')"不匹配matchExpr["CHILD"]正則表達(dá)式,故不執(zhí)行內(nèi)部語(yǔ)句體。

復(fù)制代碼 代碼如下:

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

 由于match[3] = "'",match[4] ="span",故執(zhí)行if內(nèi)部語(yǔ)句體,將"span"賦予match[2]

復(fù)制代碼 代碼如下:

return match.slice(0, 3); 

返回match前三個(gè)元素的副本
此時(shí)回到tokenize方法的for循環(huán)內(nèi)繼續(xù)執(zhí)行,此時(shí)各變量值如下:

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

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

復(fù)制代碼 代碼如下:

matched = match.shift(); 

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

復(fù)制代碼 代碼如下:

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


 創(chuàng)建一個(gè)新對(duì)象{ value:
":contain('span')", type:"PSEUDO", matches: ["contain", "span"] },并將該對(duì)象壓入tokens數(shù)組。

復(fù)制代碼 代碼如下:

soFar = soFar.slice(matched.length); 

soFar變量刪除":contain('span')",此時(shí),soFar="):eq(3)",之后,直至for循環(huán)結(jié)束,且再次執(zhí)行while循環(huán),也沒(méi)有一個(gè)有效選擇器,故退出while循環(huán)。

復(fù)制代碼 代碼如下:

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

 由于此時(shí)parseOnly = true,故返回此時(shí)soFar的長(zhǎng)度6,繼續(xù)執(zhí)行preFilters["PSEUDO"]的代碼

 

復(fù)制代碼 代碼如下:

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

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

復(fù)制代碼 代碼如下:

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

 計(jì)算出:not選擇器結(jié)束位置(即右括號(hào)位置)22

復(fù)制代碼 代碼如下:

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

分別計(jì)算出完整的:not選擇器字符串(match[0])和其括號(hào)內(nèi)的字符串(match[2]),分別等于:

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

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

復(fù)制代碼 代碼如下:

return match.slice(0, 3);

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

復(fù)制代碼 代碼如下:

matched = match.shift();

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

復(fù)制代碼 代碼如下:

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

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

復(fù)制代碼 代碼如下:

soFar = soFar.slice(matched.length); 

 soFar變量刪除":not(.class:contain('span'))",此時(shí),soFar=":eq(3)",結(jié)束本次for循環(huán)后,再次回到while循環(huán),同樣方式,獲取tokens的第三個(gè)元素eq選擇器,過(guò)程與not一致,這里就不再細(xì)講了。最后的groups的結(jié)果如下:
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"] }

復(fù)制代碼 代碼如下:

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

由于parseOnly = undefined,所以執(zhí)行tokenCache(selector, groups).slice(0),該語(yǔ)句將groups壓入緩存,并返回其副本。
由此,完成了所有的解析,或許有人會(huì)問(wèn),這里第二個(gè)元素并沒(méi)有解析出來(lái)呀,是的,這個(gè)需要在實(shí)際運(yùn)行中再次解析。當(dāng)然,這里若可以將剛才解析."class:contain('span')):eq(3"時(shí),將有效選擇器的結(jié)果保存到緩存內(nèi),那么就可以避免再次解析,提高執(zhí)行速度。但這也僅僅提高了當(dāng)前這次運(yùn)行速度。因?yàn)樵趫?zhí)行過(guò)程中,對(duì)".class:contain('span')"再次提交解析時(shí),會(huì)存入緩存。

至此,整個(gè)執(zhí)行過(guò)程已經(jīng)全部結(jié)束。

相關(guān)文章

  • jQuery屬性選擇器用法示例

    jQuery屬性選擇器用法示例

    這篇文章主要介紹了jQuery屬性選擇器用法,結(jié)合實(shí)例形式分析了jQuery中針對(duì)屬性的判斷與匹配相關(guān)操作技巧,需要的朋友可以參考下
    2016-09-09
  • jQuery分組選擇器簡(jiǎn)單用法示例

    jQuery分組選擇器簡(jiǎn)單用法示例

    這篇文章主要介紹了jQuery分組選擇器簡(jiǎn)單用法,結(jié)合實(shí)例形式分析了使用jQuery分組選擇器動(dòng)態(tài)設(shè)置頁(yè)面元素屬性相關(guān)操作技巧,需要的朋友可以參考下
    2019-04-04
  • jquery ajax分頁(yè)插件的簡(jiǎn)單實(shí)現(xiàn)

    jquery ajax分頁(yè)插件的簡(jiǎn)單實(shí)現(xiàn)

    這篇文章主要介紹了jquery ajax分頁(yè)插件的簡(jiǎn)單實(shí)現(xiàn)的相關(guān)資料,需要的朋友可以參考下
    2016-01-01
  • jQuery1.6 類型判斷實(shí)現(xiàn)代碼

    jQuery1.6 類型判斷實(shí)現(xiàn)代碼

    jQuery1.6 類型判斷實(shí)現(xiàn)代碼,學(xué)習(xí)jquery的朋友可以參考下。
    2011-09-09
  • jquery中用函數(shù)來(lái)設(shè)置css樣式

    jquery中用函數(shù)來(lái)設(shè)置css樣式

    本文主要對(duì)jquery中用函數(shù)來(lái)設(shè)置css樣式的方法進(jìn)行詳細(xì)介紹,具有很好的參考價(jià)值,下面就跟小編一起來(lái)看下吧
    2016-12-12
  • jquery ui對(duì)話框?qū)嵗a

    jquery ui對(duì)話框?qū)嵗a

    在網(wǎng)頁(yè)設(shè)計(jì)中,不論是為了減少于由于頁(yè)面跳轉(zhuǎn)而帶來(lái)的不友好用戶體驗(yàn),還是為了維持桌面應(yīng)用程序的使用習(xí)慣,對(duì)話框的設(shè)計(jì)都是不可或缺的。
    2013-05-05
  • jQuery+PHP+Mysql實(shí)現(xiàn)抽獎(jiǎng)程序

    jQuery+PHP+Mysql實(shí)現(xiàn)抽獎(jiǎng)程序

    這篇文章主要為大家詳細(xì)介紹了jQuery+PHP+Mysql實(shí)現(xiàn)抽獎(jiǎng)程序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-05-05
  • jQuery 獲取瀏覽器所在的IP地址的小例子

    jQuery 獲取瀏覽器所在的IP地址的小例子

    這篇文章介紹了jQuery 獲取瀏覽器所在的IP地址的小例子,有需要的朋友可以參考一下
    2013-11-11
  • 如何理解jQuery中的ajaxSubmit方法

    如何理解jQuery中的ajaxSubmit方法

    本文主要介紹了jQuery中的ajaxSubmit方法的相關(guān)知識(shí)。具有很好的參考價(jià)值。下面跟著小編一起來(lái)看下吧
    2017-03-03
  • AspNet中使用JQuery boxy插件的確認(rèn)框

    AspNet中使用JQuery boxy插件的確認(rèn)框

    Boxy是一個(gè)基于JQuery的彈出層插件,它有相對(duì)漂亮的外觀,功能齊全,支持iframe,支持模式窗口但相對(duì)于同樣的彈出層插件BlockUI它明顯笨重,但使用不那么方便。 兼容瀏覽器:IE6+/Firefox/Google Chrome
    2015-05-05

最新評(píng)論

邛崃市| 潢川县| 泌阳县| 屏东县| 甘德县| 扎囊县| 咸阳市| 高清| 射洪县| 闸北区| 宁陕县| 朝阳县| 龙江县| 正安县| 德惠市| 金堂县| 左权县| 工布江达县| 綦江县| 新和县| 承德县| 九寨沟县| 九江市| 固镇县| 板桥市| 米林县| 安新县| 牡丹江市| 白山市| 大城县| 海晏县| 白玉县| 绍兴市| 郯城县| 黄山市| 阿拉善右旗| 上饶县| 稷山县| 大渡口区| 睢宁县| 南川市|