自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

jQuery 2.0.3源碼分析Sizzle引擎

開發(fā) 前端
從Sizzle1.8開始,這是Sizzle的分界線了,引入了編譯函數(shù)機制,網(wǎng)上基本沒有資料細說這個東東的,Sizzle引入這個實現(xiàn)主要的作用是分詞的篩選,提高逐個匹配的效率,我們不直接看代碼的實現(xiàn),通過簡單的實現(xiàn)描述下原理。

什么是JavaScript的“預(yù)編譯”?

  1. function Aaron() { 
  2.     alert("hello"); 
  3. }; 
  4. Aaron(); //這里調(diào)用Aaron,輸出world而不是hello 
  5.  
  6. function Aaron() { 
  7.     alert("world"); 
  8. }; 
  9. Aaron(); //這里調(diào)用Aaron,當然輸出world 

按理說,兩個簽名完全相同的函數(shù),在其他編程語言中應(yīng)該是非法的。但在JavaScript中,這沒錯。不過,程序運行之后卻發(fā)現(xiàn)一個奇怪的現(xiàn)象:兩次調(diào)用都只是***那個函數(shù)里輸出的值!顯然***個函數(shù)沒有起到任何作用。這又是為什么呢?

JavaScript執(zhí)行引擎并非一行一行地分析和執(zhí)行程序,而是一段一段地進行預(yù)編譯后讓后 再執(zhí)行的。而且,在同一段程序中,函數(shù) 在被執(zhí)行之前 會被預(yù)定義,后定定義的 同名函數(shù) 會覆蓋 先定義的函數(shù)。在調(diào)用函數(shù)的時候,只會調(diào)用后一個預(yù)定義的函數(shù)(因為后一個預(yù)定義的函數(shù)把前一個預(yù)定義的函數(shù)覆蓋了)。也就是說,在***次調(diào)用myfunc之前,***個函數(shù)語句定義的代碼邏輯,已被第二個函數(shù)定義語句覆蓋了。所以,兩次都調(diào)用都是執(zhí)行***一個函數(shù)邏輯了。

我們用實際證明下:

  1. //***段代碼 
  2. <script> 
  3.     function Aaron() { 
  4.         alert("hello"); 
  5.     }; 
  6.     Aaron(); //hello 
  7. </script> 
  8.  
  9. //第二段代碼 
  10. <script> 
  11.     function Aaron() { 
  12.         alert("world"); 
  13.     }; 
  14.     Aaron(); //world 
  15. </script> 

一段代碼中的定義式函數(shù)語句會優(yōu)先執(zhí)行,這似乎有點象靜態(tài)語言的編譯概念。所以,這一特征也被有些人稱為:JavaScript的“預(yù)編譯”

所以總結(jié)下:JS 解析器在執(zhí)行語句前會將函數(shù)聲明和變量定義進行"預(yù)編譯",而這個"預(yù)編譯",并非一個頁面一個頁面地"預(yù)編譯",而是一段一段地預(yù)編譯,所謂的段就是一 個 <script> 塊。

那么我們再來看看

什么是編譯函數(shù)?

這個概念呢,我只用自己的語言表述下吧,先看看我在實際項目中的一種使用吧~

這里大概介紹下,偶做的是phonegap項目,基本實現(xiàn)了一套ppt的模板動畫

PPT的的功能設(shè)置(支持生成3個平臺的應(yīng)用)

5M32C932CWKMGQH_WC_thumb1 6A`{YX`%}M)XF0OMPE4T(JV_thumb[2]

通過這個PPT直接描述出用戶行為的數(shù)據(jù),然后直接打包生成相對應(yīng)的實現(xiàn)應(yīng)用了,實現(xiàn)部分是JS+CSS3+html5 ,關(guān)鍵是可以跨平臺哦

PC上的效果

頁面的元素都是動態(tài)的可運行可以交互的

image_thumb7 

移動端的效果

image_thumb10

編譯出來的的APK

image_thumb8

通過一套PPT軟件生成的,頁面有大量的動畫,聲音,視頻,路徑動畫,交互,拖動 等等效果,這里不細說了,那么我引入編譯函數(shù)這個概念我是用來干什么事呢?

一套大的體系,流程控制是非常重要的,簡單的來說呢就是在某個階段該干哪一件事件了

但是JS呢其實就是一套異步編程的模型

編寫異步代碼是時常的事,比如有常見的異步操作:

Ajax(XMLHttpRequest)

Image Tag,Script Tag,iframe(原理類似)

setTimeout/setInterval

CSS3 Transition/Animation

HTML5 Web Database

postMessage

Web Workers

Web Sockets

and more…

JavaScript是一門單線程語言,因此一旦有某個API阻塞了當前線程,就相當于阻塞了整個程序,所以“異步”在JavaScript編程中占有很重要的地位。異步編程對程序執(zhí)行效果的好處這里就不多談了,但是異步編程對于開發(fā)者來說十分麻煩,它會將程序邏輯拆分地支離破碎,語義完全丟失。因此,許多程序員都在打造一些異步編程模型已經(jīng)相關(guān)的API來簡化異步編程工作,例如Promise模型

現(xiàn)在有的異步流程控制大多是基于CommonJS Promises規(guī)范,比如  jsdeferred,jQuery自己的deferred等等

從用戶角度來說呢,越是功能強大的庫,則往往意味著更多的API,以及更多的學(xué)習(xí)時間,這樣開發(fā)者才能根據(jù)自身需求選擇最合適的方法

從開發(fā)者角度,API的粒度問題,粒度越大的API往往功能越強,可以通過少量的調(diào)用完成大量工作,但粒度大往往意味著難以復(fù)用。越細粒度的API靈活度往往越高,可以通過有限的API組合出足夠的靈活性,但組合是需要付出“表現(xiàn)力”作為成本的。JavaScript在表現(xiàn)力方面有一些硬傷。

好像這里有點偏題了,總的來說呢,各種異步編程模型都是種抽象,它們是為了實現(xiàn)一些常用的異步編程模式而設(shè)計出來的一套有針對性的API。但是,在實際使用過程中我們可能遇到千變?nèi)f化的問題,一旦遇到模型沒有“正面應(yīng)對”的場景,或是觸及這種模型的限制,開發(fā)人員往往就只能使用一些相對較為丑陋的方式來“回避問題”

那么在我們實際的開發(fā)中呢,我們用JS表達一段邏輯,由于在各種環(huán)境上存在著各種不同的異步情景,代碼執(zhí)行流程會在這里“暫停”,等待該異步操作結(jié)束,然后再繼續(xù)執(zhí)行后續(xù)代碼

如果是這樣的情況

  1. var a = 1;  setTimeout(function(){     a++; },1000)  alert(a)//1 

這段代碼很簡單,但是結(jié)果確不是我們想要的,我們修改一下

  1. var a = 1;  var b = function(callback) {     setTimeout(function() {        a++;         callback();     }, 1000) }  b(function(){     alert(a)  //2 }) 

任何一個普通的JavaScript程序員都能順利理解這段代碼的含義,這里的“回調(diào)”并不是“阻塞”,而會空出執(zhí)行線程,直至操作完成。而且,假如系統(tǒng)本身沒有提供阻塞的API,我們甚至沒有“阻塞”代碼的方法(當然,本就不該阻塞)。

到底編譯函數(shù)這個概念是干嘛?

JavaScript是單線程的,代碼也是同步從上向下執(zhí)行的,執(zhí)行流程不會隨便地暫停,當遇到異步的情況,從而改變了整個執(zhí)行流程的時候,我們需要對代碼進行自動改寫,也就是在程序的執(zhí)行過程中動態(tài)生成并執(zhí)行新的代碼,這個過程我想稱之為編譯函數(shù)的一種運用吧.

我個人理解嘛,這里只是一個概念而已,閉包的一種表現(xiàn)方式,就像MVVM的angular就搞出一堆的概念,什么HTML編譯器,指令,表達式,依賴注入等等,當然是跟Javaer有關(guān)系…

這里回到我之前的項目上面,我個人引入這個編譯函數(shù),是為了解決在流程中某個環(huán)節(jié)中因為異步導(dǎo)致的整個流程的執(zhí)行出錯,所以在JS異步之后,我會把整個同步代碼編譯成一個閉包函數(shù),因為這樣可以保留整個作用域的訪問,這樣等異步處理完畢之后,直接調(diào)用這個編譯函數(shù)進行匹配即可,這樣在異步的階段,同步的代碼也同時被處理了

其實說白了,就是一種閉包的使用,只是在不同的場景中換了一個優(yōu)雅的詞匯罷了, 那么在sizzle中,引入這個編譯函數(shù)是解決什么問題了?

sizzle編譯函數(shù)

文章開頭就提到了,sizzle引入這個實現(xiàn)主要的作用是分詞的篩選,提高逐個匹配的效率

這里接著上一章節(jié) 解析原理

我們在經(jīng)過詞法分析,簡單過濾,找到適合的種子集合之后

最終的選擇器抽出了input這個種子合集seed 

重組的選擇器selector

  1. div > p + div.aaron input[type="checkbox"

還有詞法分析合集 group

Sizzle中的元匹配器

通過tokenize最終分類出來的group分別都有對應(yīng)的幾種type

image

每一種type都會有對應(yīng)的處理方法

  1. Expr.filter = {    
  2.  ATTR   : function (name, operator, check) {     
  3. CHILD  : function (type, what, argument, first, last) {     
  4. CLASS  : function (className) {    
  5.  ID     : function (id) {     
  6. PSEUDO : function (pseudo, argument) {  
  7.    TAG    : function (nodeNameSelector) { } 

可以把“元”理解為“原子”,也就是最小的那個匹配器。每條選擇器規(guī)則最小的幾個單元可以劃分為:ATTR | CHILD | CLASS | ID | PSEUDO | TAG
在Sizzle里邊有一些工廠方法用來生成對應(yīng)的這些元匹配器,它就是Expr.filter。
舉2個例子(ID類型的匹配器由Expr.filter["ID"]生成,應(yīng)該是判斷elem的id屬性跟目標屬性是否一致),

拿出2個源碼

  1. //ID元匹配器工廠 
  2. Expr.filter["ID"] =  function( id ) { 
  3.   var attrId = id.replace( runescape, funescape ); 
  4.   //生成一個匹配器, 
  5.   return function( elem ) { 
  6.     var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); 
  7.     //去除節(jié)點的id,判斷跟目標是否一致 
  8.     return node && node.value === attrId; 
  9.   }; 
  10. }; 
  1. //屬性元匹配器工廠 
  2. //name :屬性名 
  3. //operator :操作符 
  4. //check : 要檢查的值 
  5. //例如選擇器 [type="checkbox"]中,name="type" operator="=" check="checkbox" 
  6. "ATTR"function(name, operator, check) { 
  7.     //返回一個元匹配器 
  8.     return function(elem) { 
  9.         //先取出節(jié)點對應(yīng)的屬性值 
  10.         var result = Sizzle.attr(elem, name); 
  11.  
  12.          //看看屬性值有木有! 
  13.         if (result == null) { 
  14.             //如果操作符是不等號,返回真,因為當前屬性為空 是不等于任何值的 
  15.             return operator === "!="
  16.         } 
  17.         //如果沒有操作符,那就直接通過規(guī)則了 
  18.         if (!operator) { 
  19.             return true
  20.         } 
  21.  
  22.         result += ""
  23.  
  24.         //如果是等號,判斷目標值跟當前屬性值相等是否為真 
  25.         return operator === "=" ? result === check : 
  26.            //如果是不等號,判斷目標值跟當前屬性值不相等是否為真 
  27.             operator === "!=" ? result !== check : 
  28.             //如果是起始相等,判斷目標值是否在當前屬性值的頭部 
  29.             operator === "^=" ? check && result.indexOf(check) === 0 : 
  30.             //這樣解釋: lang*=en 匹配這樣 <html lang="xxxxenxxx">的節(jié)點 
  31.             operator === "*=" ? check && result.indexOf(check) > -1 : 
  32.             //如果是末尾相等,判斷目標值是否在當前屬性值的末尾 
  33.             operator === "$=" ? check && result.slice(-check.length) === check : 
  34.             //這樣解釋: lang~=en 匹配這樣 <html lang="zh_CN en">的節(jié)點 
  35.             operator === "~=" ? (" " + result + " ").indexOf(check) > -1 : 
  36.             //這樣解釋: lang=|en 匹配這樣 <html lang="en-US">的節(jié)點 
  37.             operator === "|=" ? result === check || result.slice(0, check.length + 1) === check + "-" : 
  38.             //其他情況的操作符號表示不匹配 
  39.             false
  40.     }; 
  41. }, 

到這里應(yīng)該想到Sizzle其實是不是就是通過對selector做“分詞”,打散之后再分別從Expr.filter 里面去找對應(yīng)的方法來執(zhí)行具體的查詢或者過濾的操作?

答案基本是肯定的

但是這樣常規(guī)的做法邏輯上是OK的,但是效率如何?

所以Sizzle有更具體和巧妙的做法

Sizzle在這里引入了 編譯函數(shù)的概念

通過Sizzle.compile方法內(nèi)部的,

matcherFromTokens matcherFromGroupMatchers

把分析關(guān)系表,生成用于匹配單個選擇器群組的函數(shù)

matcherFromTokens,它充當了selector“分詞”與Expr中定義的匹配方法的串聯(lián)與紐帶的作用,可以說選擇符的各種排列組合都是能適應(yīng)的了。Sizzle巧妙的就是它沒有直接將拿到的“分詞”結(jié)果與Expr中的方法逐個匹配逐個執(zhí)行,而是先根據(jù)規(guī)則組合出一個大的匹配方法,***一步執(zhí)行

我們看看如何用matcherFromTokens來生成對應(yīng)Token的匹配器?

先貼源碼 

Sizzle.compile

  1. //編譯函數(shù)機制 
  2.         //通過傳遞進來的selector和match生成匹配器: 
  3.         compile = Sizzle.compile = function(selector, group /* Internal Use Only */ ) { 
  4.             var i, 
  5.                  setMatchers = [], 
  6.                  elementMatchers = [], 
  7.                  cached = compilerCache[selector + " "]; 
  8.              if (!cached) { //依舊看看有沒有緩存 
  9.                  // Generate a function of recursive functions that can be used to check each element 
  10.                 if (!group) { 
  11.                     //如果沒有詞法解析過 
  12.                     group = tokenize(selector); 
  13.                 } 
  14.                 i = group.length; //從后開始生成匹配器 
  15.                 //如果是有并聯(lián)選擇器這里多次等循環(huán) 
  16.                 while (i--) { 
  17.                     //這里用matcherFromTokens來生成對應(yīng)Token的匹配器 
  18.                     cached = matcherFromTokens(group[i]); 
  19.                     if (cached[expando]) { 
  20.                         setMatchers.push(cached); 
  21.                     } else { //普通的那些匹配器都壓入了elementMatchers里邊 
  22.                         elementMatchers.push(cached); 
  23.                     } 
  24.                 } 
  25.                 // Cache the compiled function 
  26.                 // 這里可以看到,是通過matcherFromGroupMatchers這個函數(shù)來生成最終的匹配器 
  27.                 cached = compilerCache(selector, matcherFromGroupMatchers(elementMatchers, setMatchers)); 
  28.             } 
  29.             //把這個***匹配器返回到select函數(shù)中 
  30.             return cached; 
  31.         }; 

matcherFromTokens

 

  1.  1:      //生成用于匹配單個選擇器組的函數(shù) 
  2.  2:      //充當了selector“tokens”與Expr中定義的匹配方法的串聯(lián)與紐帶的作用, 
  3.  3:      //可以說選擇符的各種排列組合都是能適應(yīng)的了 
  4.  4:      //Sizzle巧妙的就是它沒有直接將拿到的“分詞”結(jié)果與Expr中的方法逐個匹配逐個執(zhí)行, 
  5.  5:      //而是先根據(jù)規(guī)則組合出一個大的匹配方法,***一步執(zhí)行。但是組合之后怎么執(zhí)行的 
  6.  6:      function matcherFromTokens(tokens) { 
  7.  7:          var checkContext, matcher, j, 
  8.  8:              len = tokens.length, 
  9.  9:              leadingRelative = Expr.relative[tokens[0].type], 
  10. 10:              implicitRelative = leadingRelative || Expr.relative[" "], //親密度關(guān)系 
  11. 11:              i = leadingRelative ? 1 : 0, 
  12. 12:    
  13. 13:              // The foundational matcher ensures that elements are reachable from top-level context(s) 
  14. 14:              // 確保這些元素可以在context中找到 
  15. 15:              matchContext = addCombinator(function(elem) { 
  16. 16:                  return elem === checkContext; 
  17. 17:              }, implicitRelative, true), 
  18. 18:              matchAnyContext = addCombinator(function(elem) { 
  19. 19:                  return indexOf.call(checkContext, elem) > -1; 
  20. 20:              }, implicitRelative, true), 
  21. 21:    
  22. 22:              //這里用來確定元素在哪個context 
  23. 23:              matchers = [ 
  24. 24:                  function(elem, context, xml) { 
  25. 25:                      return (!leadingRelative && (xml || context !== outermostContext)) || ( 
  26. 26:                          (checkContext = context).nodeType ? 
  27. 27:                          matchContext(elem, context, xml) : 
  28. 28:                          matchAnyContext(elem, context, xml)); 
  29. 29:                  } 
  30. 30:              ]; 
  31. 31:    
  32. 32:          for (; i < len; i++) { 
  33. 33:              // Expr.relative 匹配關(guān)系選擇器類型 
  34. 34:              // "空 > ~ +" 
  35. 35:              if ((matcher = Expr.relative[tokens[i].type])) { 
  36. 36:                  //當遇到關(guān)系選擇器時elementMatcher函數(shù)將matchers數(shù)組中的函數(shù)生成一個函數(shù) 
  37. 37:                  //(elementMatcher利用了閉包所以matchers一直存在內(nèi)存中) 
  38. 38:                  matchers = [addCombinator(elementMatcher(matchers), matcher)]; 
  39. 39:              } else { 
  40. 40:                  //過濾  ATTR CHILD CLASS ID PSEUDO TAG 
  41. 41:                  matcher = Expr.filter[tokens[i].type].apply(null, tokens[i].matches); 
  42. 42:    
  43. 43:                  // Return special upon seeing a positional matcher 
  44. 44:                  //返回一個特殊的位置匹配函數(shù) 
  45. 45:                  //偽類會把selector分兩部分 
  46. 46:                  if (matcher[expando]) { 
  47. 47:                      // Find the next relative operator (if any) for proper handling 
  48. 48:                      // 發(fā)現(xiàn)下一個關(guān)系操作符(如果有話)并做適當處理 
  49. 49:                      j = ++i; 
  50. 50:                      for (; j < len; j++) { 
  51. 51:                          if (Expr.relative[tokens[j].type]) { //如果位置偽類后面還有關(guān)系選擇器還需要篩選 
  52. 52:                              break; 
  53. 53:                          } 
  54. 54:                      } 
  55. 55:                      return setMatcher( 
  56. 56:                          i > 1 && elementMatcher(matchers), 
  57. 57:                          i > 1 && toSelector( 
  58. 58:                              // If the preceding token was a descendant combinator, insert an implicit any-element `*` 
  59. 59:                              tokens.slice(0, i - 1).concat({ 
  60. 60:                                  value: tokens[i - 2].type === " " ? "*" : "" 
  61. 61:                              }) 
  62. 62:                          ).replace(rtrim, "$1"), 
  63. 63:                          matcher, 
  64. 64:                          i < j && matcherFromTokens(tokens.slice(i, j)), //如果位置偽類后面還有選擇器需要篩選 
  65. 65:                          j < len && matcherFromTokens((tokenstokens = tokens.slice(j))), //如果位置偽類后面還有關(guān)系選擇器還需要篩選 
  66. 66:                          j < len && toSelector(tokens) 
  67. 67:                      ); 
  68. 68:                  } 
  69. 69:                  matchers.push(matcher); 
  70. 70:              } 
  71. 71:          } 
  72. 72:    
  73. 73:          return elementMatcher(matchers); 
  74. 74:      } 
 

重點就是

  1. cached = matcherFromTokens(group[i]); 

cached 的結(jié)果就是matcherFromTokens返回的matchers編譯函數(shù)了

matcherFromTokens的分解是有規(guī)律的:

語義節(jié)點+關(guān)系選擇器的組合

  1. div > p + div.aaron input[type="checkbox"

Expr.relative 匹配關(guān)系選擇器類型

當遇到關(guān)系選擇器時elementMatcher函數(shù)將matchers數(shù)組中的函數(shù)生成一個函數(shù)

在遞歸分解tokens中的詞法元素時

提出***個typ匹配到對應(yīng)的處理方法

  1. matcher = Expr.filter[tokens[i].type].apply(null, tokens[i].matches); 
  2.  
  3. "TAG": function(nodeNameSelector) {                 
  4. var nodeName = nodeNameSelector.replace(runescape, funescape).toLowerCase();    
  5.              return nodeNameSelector === "*" ? 
  6.                     function() {                  
  7.        return true;     
  8.  
  9.             } :                     
  10. function(elem) {        
  11.     return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;   
  12.               };             }, 

matcher其實最終結(jié)果返回的就是bool值,但是這里返回只是一個閉包函數(shù),不會馬上執(zhí)行,這個過程換句話就是 編譯成一個匿名函數(shù)

繼續(xù)往下分解

如果遇到關(guān)系選著符就會合并分組了

  1. matchers = [addCombinator(elementMatcher(matchers), matcher)]; 

通過elementMatcher生成一個***匹配器

  1. function elementMatcher(matchers) { 
  2.         //生成一個***匹配器 
  3.         return matchers.length > 1 ? 
  4.         //如果是多個匹配器的情況,那么就需要elem符合全部匹配器規(guī)則 
  5.             function(elem, context, xml) { 
  6.                 var i = matchers.length; 
  7.                 //從右到左開始匹配 
  8.                 while (i--) { 
  9.                     //如果有一個沒匹配中,那就說明該節(jié)點elem不符合規(guī)則 
  10.                     if (!matchers[i](elem, context, xml)) { 
  11.                         return false
  12.                     } 
  13.                 } 
  14.                 return true
  15.         } : 
  16.         //單個匹配器的話就返回自己即可 
  17.             matchers[0]; 
  18.     } 

看代碼大概就知道,就是分解這個子匹配器了,返回又一個curry函數(shù),給addCombinator方法

  1. //addCombinator方法就是為了生成有位置詞素的匹配器。 
  2.     function addCombinator(matcher, combinator, base) { 
  3.         var dir = combinator.dir, 
  4.             checkNonElements = base && dir === "parentNode", 
  5.             donedoneName = done++; //第幾個關(guān)系選擇器 
  6.  
  7.         return combinator.first ? 
  8.         // Check against closest ancestor/preceding element 
  9.         // 檢查最靠近的祖先元素 
  10.         // 如果是緊密關(guān)系的位置詞素 
  11.         function(elem, context, xml) { 
  12.             while ((elemelem = elem[dir])) { 
  13.                 if (elem.nodeType === 1 || checkNonElements) { 
  14.                     //找到***個親密的節(jié)點,立馬就用***匹配器判斷這個節(jié)點是否符合前面的規(guī)則 
  15.                     return matcher(elem, context, xml); 
  16.                 } 
  17.             } 
  18.         } : 
  19.  
  20.         // Check against all ancestor/preceding elements 
  21.         //檢查最靠近的祖先元素或兄弟元素(概據(jù)>、~、+還有空格檢查) 
  22.         //如果是不緊密關(guān)系的位置詞素 
  23.         function(elem, context, xml) { 
  24.             var data, cache, outerCache, 
  25.                 dirkey = dirruns + " " + doneName; 
  26.  
  27.             // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching 
  28.             // 我們不可以在xml節(jié)點上設(shè)置任意數(shù)據(jù),所以它們不會從dir緩存中受益 
  29.             if (xml) { 
  30.                 while ((elemelem = elem[dir])) { 
  31.                     if (elem.nodeType === 1 || checkNonElements) { 
  32.                         if (matcher(elem, context, xml)) { 
  33.                             return true; 
  34.                         } 
  35.                     } 
  36.                 } 
  37.             } else { 
  38.                 while ((elemelem = elem[dir])) { 
  39.                     //如果是不緊密的位置關(guān)系 
  40.                     //那么一直匹配到true為止 
  41.                     //例如祖宗關(guān)系的話,就一直找父親節(jié)點直到有一個祖先節(jié)點符合規(guī)則為止 
  42.                     if (elem.nodeType === 1 || checkNonElements) { 
  43.                         outerCache = elem[expando] || (elem[expando] = {}); 
  44.                         //如果有緩存且符合下列條件則不用再次調(diào)用matcher函數(shù) 
  45.                         if ((cache = outerCache[dir]) && cache[0] === dirkey) { 
  46.                             if ((data = cache[1]) === true || data === cachedruns) { 
  47.                                 return data === true; 
  48.                             } 
  49.                         } else { 
  50.                             cache = outerCache[dir] = [dirkey]; 
  51.                             cache[1] = matcher(elem, context, xml) || cachedruns; //cachedruns//正在匹配第幾個元素 
  52.                             if (cache[1] === true) { 
  53.                                 return true; 
  54.                             } 
  55.                         } 
  56.                     } 
  57.                 } 
  58.             } 
  59.         }; 
  60.     } 

matcher為當前詞素前的“***匹配器”

combinator為位置詞素

根據(jù)關(guān)系選擇器檢查

如果是這類沒有位置詞素的選擇器:’#id.aaron[name="checkbox"]‘

從右到左依次看看當前節(jié)點elem是否匹配規(guī)則即可。但是由于有了位置詞素,

那么判斷的時候就不是簡單判斷當前節(jié)點了,

可能需要判斷elem的兄弟或者父親節(jié)點是否依次符合規(guī)則。

這是一個遞歸深搜的過程。

所以matchers又經(jīng)過一層包裝了

然后用同樣的方式遞歸下去,直接到tokens分解完畢

返回的結(jié)果一個根據(jù)關(guān)系選擇器分組后在組合的嵌套很深的閉包函數(shù)了

看看結(jié)構(gòu)

image

 

但是組合之后怎么執(zhí)行?

superMatcher方法是matcherFromGroupMatchers( elementMatchers, setMatchers )方法return出來的,但是***執(zhí)行起重要作用的是它

下章在繼續(xù),這章主要只是要說說這個編譯函數(shù)的流程,具體還有細節(jié),就需要仔細看代碼,我不能一條一條去分解的,還有函數(shù)具體的用處,就需要結(jié)合后面的才能比較好的理解!

原文鏈接:http://www.cnblogs.com/aaronjs/p/3322466.html

特此感謝Aaron。

【編輯推薦】

  1. 拋磚引玉 自定義jQuery擴展接口
  2. jQuery四大天王:核心函數(shù)詳解
  3. 改變獲取對象方式 ***的jQuery選擇器
責(zé)任編輯:彭凡 來源: 博客園
相關(guān)推薦

2010-07-20 10:11:32

jQuery選擇器Sizzle

2013-10-10 14:52:53

jQueryDeferred

2012-09-06 10:07:26

jQuery

2014-08-26 11:11:57

AsyncHttpCl源碼分析

2011-03-15 11:33:18

iptables

2011-05-26 10:05:48

MongoDB

2012-11-06 11:07:59

jQueryJSjQuery框架

2013-05-27 14:37:31

Hadoop 2.0.

2021-11-11 17:40:08

WatchdogAndroid源碼分析

2022-08-30 07:00:18

執(zhí)行引擎Hotspot虛擬機

2011-05-26 16:18:51

Mongodb

2010-08-26 17:08:20

vsftpd faq

2012-02-01 16:08:05

JavajOOQ

2010-01-05 15:55:33

JQuery源碼

2012-07-10 09:34:50

jQuery

2015-08-10 15:12:27

Java實例源碼分析

2021-03-23 09:17:58

SpringMVCHttpServletJavaEE

2021-07-06 09:29:38

Cobar源碼AST

2024-06-13 07:55:19

2021-09-06 10:34:48

Nacos復(fù)制源碼
點贊
收藏

51CTO技術(shù)棧公眾號