国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 語言 > JavaScript > 正文

jQuery選擇器源碼解讀(三):tokenize方法

2024-05-06 16:17:54
字體:
來源:轉載
供稿:網友

這篇文章主要介紹了jQuery選擇器源碼解讀(三):tokenize方法,本文用詳細的注釋解讀了tokenize方法的實現源碼,需要的朋友可以參考下

 

 
  1. /* 
  2. * tokenize方法是選擇器解析的核心函數,它將選擇器轉換成兩級數組groups 
  3. * 舉例: 
  4. * 若選擇器為“div.class,span”,則解析后的結果為: 
  5. * group[0][0] = {type:'TAG',value:'div',matches:match} 
  6. * group[0][1] = {type:'CLASS',value:'.class',matches:match} 
  7. * group[1][0] = {type:'TAG',value:'span',matches:match} 
  8. * 由上述結果可以看出,groups的每一個元素以逗號分隔的選擇器塊的解析結果, 
  9. * 另外,上述結果中的matches等于模式匹配的結果,由于在此不方便寫清楚, 
  10. * 故只把代碼matches:match寫在這里。 
  11.  
  12. * tokenize方法完成如下兩個主要任務: 
  13. * 1、解析選擇器 
  14. * 2、將解析結果存入緩存中,以備后用 
  15.  
  16.  
  17. * @param selector 待解析的選擇器字符串 
  18. * @param parseOnly 為true時,說明本次調用是匹配子選擇器 
  19. * 舉個例子:若初始選擇器為"div:not(.class:not(:eq(4))):eq(3)" 
  20. * 代碼首先匹配出TAG選擇器div, 
  21. * 之后匹配出的pseudo選擇器字符串是:not(.class:not(:eq(4))):eq(3), 
  22. * 代碼會把“.class:not(:eq(4))):eq(3”作為not的括號內的值進一步進行解析, 
  23. * 此時代碼在調用tokenize解析時,parseOnly參數會傳入true. 
  24. */ 
  25. function tokenize(selector, parseOnly) { 
  26. var matched, match, tokens, type, soFar, groups, preFilters,  
  27. // 獲取緩存中的結果 
  28. cached = tokenCache[selector + " "]; 
  29.  
  30. /* 
  31. * 若緩存中有selector對應的解析結果 
  32. * 則執行if中語句體 
  33. */ 
  34. if (cached) { 
  35. // 若是對初始選擇器解析(parseOnly!=true),則返回緩存結果, 
  36. // 若不是,則返回0 
  37. return parseOnly ? 0 : cached.slice(0); 
  38.  
  39. /* 
  40. * 由于字符串在javascript中不是作為對象來處理的, 
  41. * 所以通過賦值,代碼就自動復制了一個新字符串給了soFar, 
  42. * 這樣,對soFar的任何處理都不會影響selector的原有數據 
  43. */ 
  44. soFar = selector; 
  45. groups = []; 
  46. // 此處賦值,僅僅用于減少后續代碼字數,縮短執行路徑 
  47. preFilters = Expr.preFilter; 
  48.  
  49. while (soFar) { 
  50.  
  51. // Comma and first run 
  52. /* 
  53. * rcomma = new RegExp("^" + whitespace + "*," + whitespace + "*") 
  54. * rcomma用來判定是否存在多個選擇器塊,即用逗號隔開的多個并列的選擇器 
  55.  
  56. * 下面條件判定依次為: 
  57. * !matched:若是第一次執行循環體,則為true;否則為false。 
  58. * 這里matched即作為是否第一次執行循環體的標識, 
  59. * 也作為本次循環中soFar是否以非法字符串(即非合法單一選擇器)開頭的標志。 
  60. * (match = rcomma.exec(soFar):獲取符合rcomma的匹配項 
  61. */ 
  62. if (!matched || (match = rcomma.exec(soFar))) { 
  63. if (match) { 
  64. // Don't consume trailing commas as valid 
  65. /* 
  66. * 剔除掉第一個逗號及之前的所有字符 
  67. * 舉個例子: 
  68. * 若初始選擇器為:"div.news,span.closed", 
  69. * 在解析過程中,首先由后續代碼解析完畢div.news,剩下",span.closed" 
  70. * 在循環體內執行到這里時,將逗號及之前之后連續的空白(match[0])刪除掉, 
  71. * 使soFar變成"span.closed",繼續執行解析過程 
  72.  
  73. * 在這里,若初始選擇器的最后一個非空白字符是逗號, 
  74. * 那么執行下面代碼時soFar不變,即soFar.slice(match[0].length)返回空字符串, 
  75. * 故最終返回的是||后面的soFar 
  76. */ 
  77. soFar = soFar.slice(match[0].length) || soFar; 
  78.  
  79. /* 
  80. * 在第一次執行循環體或者遇到逗號分割符時,將tokens賦值為一個空數組, 
  81. * 同時壓入groups數組 
  82. */ 
  83. groups.push(tokens = []); 
  84.  
  85. matched = false
  86.  
  87. // Combinators 
  88. /* 
  89. * rcombinators = new RegExp( 
  90. * "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*"), 
  91. * rcombinators用來匹配四種關系符,即>+~和空白 
  92.  
  93. * 若soFar中是以關系符開始的,則執行if內的語句體 
  94. */ 
  95. if ((match = rcombinators.exec(soFar))) { 
  96. /* 
  97. * 將match[0]移除match數組,同時將它賦予matched 
  98. * 若原本關系符兩邊帶有空格,則此時match[0]與matched是不相等的 
  99. * 舉個例子: 
  100. * 若soFar = " + .div"; 
  101. * 執行match = rcombinators.exec(soFar)后, 
  102. * match[0] = " + ",而match[1]="+"; 
  103. * 執行完matched = match.shift()后, 
  104. * matched=" + ",而match[0]="+"; 
  105. */ 
  106. matched = match.shift(); 
  107. // 將匹配結果壓入tokens數組中 
  108. tokens.push({ 
  109. value : matched, 
  110. // Cast descendant combinators to space 
  111. /* 
  112. * rtrim = new RegExp("^" + whitespace + "+|((?:^|[^////])(?:////.)*)" 
  113. * + whitespace + "+$", "g"), 
  114. * whitespace = "[//x20//t//r//n//f]"; 
  115.  
  116. * 下面match[0].replace(rtrim, " ")的作用是將match[0]左右兩邊的空白替換為空格 
  117. * 但是由于其上的match.shift的作用,match[0]已經是兩邊不帶空白的字符串了, 
  118. * 故此出的替換是沒有用途的代碼 
  119. */ 
  120. type : match[0].replace(rtrim, " "
  121. }); 
  122.  
  123. // 將關系符之后的字符串賦予soFar,繼續解析 
  124. soFar = soFar.slice(matched.length); 
  125.  
  126. // Filters 
  127. /* 
  128. * 下面通過for語句對soFar逐一匹配ID、TAG、CLASS、CHILD、ATTR、PSEUDO類型的選擇器 
  129. * 若匹配到了,則先調用該類型選擇器對應的預過濾函數, 
  130. * 然后,將結果壓入tokens數組,繼續本次循環。 
  131. */ 
  132. for (type in Expr.filter) { 
  133. /* 
  134. * match = matchExpr[type].exec(soFar):對soFar調用type類型的正則表達式對soFar進行匹配, 
  135. * 并將匹配結果賦予match。若未匹配到數據,則match為undefined。 
  136. * !preFilters[type]:若不存在type類型的預過濾函數,則為true 
  137. * match = preFilters[type](match):執行預過濾,并將結果返回給match 
  138.  
  139. */ 
  140. if ((match = matchExpr[type].exec(soFar)) 
  141. && (!preFilters[type] || (match = preFilters[type] 
  142. (match)))) { 
  143. // 將match[0]移除match數組,同時將它賦予matched 
  144. matched = match.shift(); 
  145. // 將匹配結果壓入tokens數組中 
  146. tokens.push({ 
  147. value : matched, 
  148. type : type, 
  149. matches : match 
  150. }); 
  151. // 將匹配結果之后的字符串賦予soFar,繼續解析 
  152. soFar = soFar.slice(matched.length); 
  153.  
  154. /* 
  155. * 若matched==false, 
  156. * 則說明本次循環沒有有效的選擇器(包括關系符和id、class等類型選擇器) 
  157. * 因此,解析到當前位置遺留下來的soFar是非法的選擇器字符串 
  158. * 跳出while循環體 
  159. */ 
  160. if (!matched) { 
  161. break
  162.  
  163. // Return the length of the invalid excess 
  164. // if we're just parsing 
  165. // Otherwise, throw an error or return tokens 
  166. /* 
  167. * 若不是對初始選擇器字符串進行解析(!parseOnly==true), 
  168. * 則返回soFar.length,此時的soFar.length代表連續有效的選擇器最終位置, 
  169. * 后續文章將以實例進行說明 
  170. * 若是對初始選擇器字符串進行解析,則看soFar是否還有字符, 
  171. * 若是,則執行Sizzle.error(selector)拋出異常; 
  172. * 若不是,則執行tokenCache(selector, groups).slice(0)將結果壓入緩存,并返回結果的副本。 
  173. */ 
  174. return parseOnly ? soFar.length : soFar ? Sizzle.error(selector) : 
  175. // Cache the tokens 
  176. tokenCache(selector, groups).slice(0); 

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表

圖片精選

主站蜘蛛池模板: 磐安县| 贵阳市| 五台县| 韩城市| 宁武县| 吴堡县| 大丰市| 涟水县| 广河县| 建始县| 石台县| 和田县| 年辖:市辖区| 宿州市| 弥勒县| 玛多县| 奉化市| 汝阳县| 陆川县| 谷城县| 台南市| 三原县| 昭觉县| 项城市| 永济市| 乌兰县| 莒南县| 黔南| 吉林省| 息烽县| 新化县| 鹤山市| 阜新| 略阳县| 托克逊县| 乐平市| 山西省| 延川县| 井冈山市| 望都县| 南乐县|