jQuery選擇器源碼解讀(三):tokenize方法
/* * tokenize方法是選擇器解析的核心函數(shù),它將選擇器轉(zhuǎn)換成兩級(jí)數(shù)組groups * 舉例: * 若選擇器為“div.class,span”,則解析后的結(jié)果為: * group[0][0] = {type:'TAG',value:'div',matches:match} * group[0][1] = {type:'CLASS',value:'.class',matches:match} * group[1][0] = {type:'TAG',value:'span',matches:match} * 由上述結(jié)果可以看出,groups的每一個(gè)元素以逗號(hào)分隔的選擇器塊的解析結(jié)果, * 另外,上述結(jié)果中的matches等于模式匹配的結(jié)果,由于在此不方便寫清楚, * 故只把代碼matches:match寫在這里。 * * tokenize方法完成如下兩個(gè)主要任務(wù): * 1、解析選擇器 * 2、將解析結(jié)果存入緩存中,以備后用 * * * @param selector 待解析的選擇器字符串 * @param parseOnly 為true時(shí),說明本次調(diào)用是匹配子選擇器 * 舉個(gè)例子:若初始選擇器為"div:not(.class:not(:eq(4))):eq(3)" * 代碼首先匹配出TAG選擇器div, * 之后匹配出的pseudo選擇器字符串是:not(.class:not(:eq(4))):eq(3), * 代碼會(huì)把“.class:not(:eq(4))):eq(3”作為not的括號(hào)內(nèi)的值進(jìn)一步進(jìn)行解析, * 此時(shí)代碼在調(diào)用tokenize解析時(shí),parseOnly參數(shù)會(huì)傳入true. */ function tokenize(selector, parseOnly) { var matched, match, tokens, type, soFar, groups, preFilters, // 獲取緩存中的結(jié)果 cached = tokenCache[selector + " "]; /* * 若緩存中有selector對(duì)應(yīng)的解析結(jié)果 * 則執(zhí)行if中語句體 */ if (cached) { // 若是對(duì)初始選擇器解析(parseOnly!=true),則返回緩存結(jié)果, // 若不是,則返回0 return parseOnly ? 0 : cached.slice(0); } /* * 由于字符串在javascript中不是作為對(duì)象來處理的, * 所以通過賦值,代碼就自動(dòng)復(fù)制了一個(gè)新字符串給了soFar, * 這樣,對(duì)soFar的任何處理都不會(huì)影響selector的原有數(shù)據(jù) */ soFar = selector; groups = []; // 此處賦值,僅僅用于減少后續(xù)代碼字?jǐn)?shù),縮短執(zhí)行路徑 preFilters = Expr.preFilter; while (soFar) { // Comma and first run /* * rcomma = new RegExp("^" + whitespace + "*," + whitespace + "*") * rcomma用來判定是否存在多個(gè)選擇器塊,即用逗號(hào)隔開的多個(gè)并列的選擇器 * * 下面條件判定依次為: * !matched:若是第一次執(zhí)行循環(huán)體,則為true;否則為false。 * 這里matched即作為是否第一次執(zhí)行循環(huán)體的標(biāo)識(shí), * 也作為本次循環(huán)中soFar是否以非法字符串(即非合法單一選擇器)開頭的標(biāo)志。 * (match = rcomma.exec(soFar):獲取符合rcomma的匹配項(xiàng) */ if (!matched || (match = rcomma.exec(soFar))) { if (match) { // Don't consume trailing commas as valid /* * 剔除掉第一個(gè)逗號(hào)及之前的所有字符 * 舉個(gè)例子: * 若初始選擇器為:"div.news,span.closed", * 在解析過程中,首先由后續(xù)代碼解析完畢div.news,剩下",span.closed" * 在循環(huán)體內(nèi)執(zhí)行到這里時(shí),將逗號(hào)及之前之后連續(xù)的空白(match[0])刪除掉, * 使soFar變成"span.closed",繼續(xù)執(zhí)行解析過程 * * 在這里,若初始選擇器的最后一個(gè)非空白字符是逗號(hào), * 那么執(zhí)行下面代碼時(shí)soFar不變,即soFar.slice(match[0].length)返回空字符串, * 故最終返回的是||后面的soFar */ soFar = soFar.slice(match[0].length) || soFar; } /* * 在第一次執(zhí)行循環(huán)體或者遇到逗號(hào)分割符時(shí),將tokens賦值為一個(gè)空數(shù)組, * 同時(shí)壓入groups數(shù)組 */ groups.push(tokens = []); } matched = false; // Combinators /* * rcombinators = new RegExp( * "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*"), * rcombinators用來匹配四種關(guān)系符,即>+~和空白 * * 若soFar中是以關(guān)系符開始的,則執(zhí)行if內(nèi)的語句體 */ if ((match = rcombinators.exec(soFar))) { /* * 將match[0]移除match數(shù)組,同時(shí)將它賦予matched * 若原本關(guān)系符兩邊帶有空格,則此時(shí)match[0]與matched是不相等的 * 舉個(gè)例子: * 若soFar = " + .div"; * 執(zhí)行match = rcombinators.exec(soFar)后, * match[0] = " + ",而match[1]="+"; * 執(zhí)行完matched = match.shift()后, * matched=" + ",而match[0]="+"; */ matched = match.shift(); // 將匹配結(jié)果壓入tokens數(shù)組中 tokens.push({ value : matched, // Cast descendant combinators to space /* * rtrim = new RegExp("^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" * + whitespace + "+$", "g"), * whitespace = "[\\x20\\t\\r\\n\\f]"; * * 下面match[0].replace(rtrim, " ")的作用是將match[0]左右兩邊的空白替換為空格 * 但是由于其上的match.shift的作用,match[0]已經(jīng)是兩邊不帶空白的字符串了, * 故此出的替換是沒有用途的代碼 */ type : match[0].replace(rtrim, " ") }); // 將關(guān)系符之后的字符串賦予soFar,繼續(xù)解析 soFar = soFar.slice(matched.length); } // Filters /* * 下面通過for語句對(duì)soFar逐一匹配ID、TAG、CLASS、CHILD、ATTR、PSEUDO類型的選擇器 * 若匹配到了,則先調(diào)用該類型選擇器對(duì)應(yīng)的預(yù)過濾函數(shù), * 然后,將結(jié)果壓入tokens數(shù)組,繼續(xù)本次循環(huán)。 */ for (type in Expr.filter) { /* * match = matchExpr[type].exec(soFar):對(duì)soFar調(diào)用type類型的正則表達(dá)式對(duì)soFar進(jìn)行匹配, * 并將匹配結(jié)果賦予match。若未匹配到數(shù)據(jù),則match為undefined。 * !preFilters[type]:若不存在type類型的預(yù)過濾函數(shù),則為true * match = preFilters[type](match):執(zhí)行預(yù)過濾,并將結(jié)果返回給match * */ if ((match = matchExpr[type].exec(soFar)) && (!preFilters[type] || (match = preFilters[type] (match)))) { // 將match[0]移除match數(shù)組,同時(shí)將它賦予matched matched = match.shift(); // 將匹配結(jié)果壓入tokens數(shù)組中 tokens.push({ value : matched, type : type, matches : match }); // 將匹配結(jié)果之后的字符串賦予soFar,繼續(xù)解析 soFar = soFar.slice(matched.length); } } /* * 若matched==false, * 則說明本次循環(huán)沒有有效的選擇器(包括關(guān)系符和id、class等類型選擇器) * 因此,解析到當(dāng)前位置遺留下來的soFar是非法的選擇器字符串 * 跳出while循環(huán)體 */ if (!matched) { break; } } // Return the length of the invalid excess // if we're just parsing // Otherwise, throw an error or return tokens /* * 若不是對(duì)初始選擇器字符串進(jìn)行解析(!parseOnly==true), * 則返回soFar.length,此時(shí)的soFar.length代表連續(xù)有效的選擇器最終位置, * 后續(xù)文章將以實(shí)例進(jìn)行說明 * 若是對(duì)初始選擇器字符串進(jìn)行解析,則看soFar是否還有字符, * 若是,則執(zhí)行Sizzle.error(selector)拋出異常; * 若不是,則執(zhí)行tokenCache(selector, groups).slice(0)將結(jié)果壓入緩存,并返回結(jié)果的副本。 */ return parseOnly ? soFar.length : soFar ? Sizzle.error(selector) : // Cache the tokens tokenCache(selector, groups).slice(0); }
- jQuery選擇器源碼解讀(二):select方法
- jQuery選擇器源碼解讀(五):tokenize的解析過程
- jQuery選擇器源碼解讀(六):Sizzle選擇器匹配邏輯分析
- jQuery選擇器源碼解讀(七):elementMatcher函數(shù)
- jquery表單對(duì)象屬性過濾選擇器實(shí)例分析
- JQuery中屬性過濾選擇器用法實(shí)例分析
- JQuery中基礎(chǔ)過濾選擇器用法實(shí)例分析
- Jquery中基本選擇器用法實(shí)例詳解
- JQuery中層次選擇器用法實(shí)例詳解
- JQuery選擇器、過濾器大整理
- jquery實(shí)現(xiàn)不包含當(dāng)前項(xiàng)的選擇器實(shí)例
- 使用jQuery在對(duì)象中緩存選擇器的簡(jiǎn)單方法
- jquery選擇器簡(jiǎn)述
相關(guān)文章
jQuery+easyui中的combobox實(shí)現(xiàn)下拉框特效
這篇文章主要介紹了jQuery+easyui中的combobox實(shí)現(xiàn)下拉框特效的幾種方法,非常簡(jiǎn)單實(shí)用,需要的朋友可以參考下2015-02-026款經(jīng)典實(shí)用的jQuery小插件及源碼(對(duì)話框/提示工具等等)
jQuery擁有豐富多彩的插件,這些插件可以幫助你簡(jiǎn)化很多的開發(fā)過程,下面介紹的6款實(shí)用jQuery小插件及源碼,感興趣的朋友可以參考下,希望本文可以幫助到你2013-02-02js制作帶有遮罩彈出層實(shí)現(xiàn)登錄注冊(cè)表單特效代碼分享
這篇文章主要為大家詳細(xì)介紹了js制作帶有遮罩彈出層實(shí)現(xiàn)登錄注冊(cè)表單代碼特效,推薦給大家,有需要的小伙伴可以參考下。2015-09-09jQuery+ajax中g(shù)etJSON() 用法實(shí)例
這篇文章主要介紹了jQuery+ajax中g(shù)etJSON() 用法實(shí)例,需要的朋友可以參考下2014-12-12jQuery動(dòng)態(tài)追加頁面數(shù)據(jù)以及事件委托詳解
這篇文章主要為大家詳細(xì)介紹了jQuery動(dòng)態(tài)追加頁面數(shù)據(jù)以及事件委托的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-05-05jQuery實(shí)現(xiàn)側(cè)邊欄隱藏與顯示的方法詳解
這篇文章主要介紹了jQuery實(shí)現(xiàn)側(cè)邊欄隱藏與顯示的方法,結(jié)合實(shí)例形式較為詳細(xì)的分析了jQuery基于事件響應(yīng)與頁面元素屬性動(dòng)態(tài)操作實(shí)現(xiàn)元素顯示與隱藏功能的相關(guān)操作技巧,需要的朋友可以參考下2018-12-12JQuery操作Select的Options的Bug(IE8兼容性視圖模式)
JQuery在IE8兼容性視圖模式下操作Select的Options的Bug在本文進(jìn)行重現(xiàn)并給出詳細(xì)的解決方法,感興趣的朋友可以參考下哈,希望對(duì)你有所幫助2013-04-04jQuery extend()詳解及簡(jiǎn)單實(shí)例
這篇文章主要介紹了jQuery extend()詳解及簡(jiǎn)單實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-05-05jQuery實(shí)現(xiàn)表格行數(shù)據(jù)滾動(dòng)效果
這篇文章主要為大家詳細(xì)介紹了jQuery實(shí)現(xiàn)表格行數(shù)據(jù)滾動(dòng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08