<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                [toc] ## Tokens 詞法分析 其實詞法分析是匯編里面提到的詞匯,把它用到這里感覺略有不合適,但 Sizzle 中的 tokensize函數干的就是詞法分析的活。 上一章我們已經講到了 Sizzle 的用法,實際上就是 jQuery.find 函數,只不過還涉及到 jQuery.fn.find。jQuery.find 函數考慮的很周到,對于處理 #id、.class 和 TagName 的情況,都比較簡單,通過一個正則表達式 rquickExpr 將內容給分開,如果瀏覽器支持 querySelectorAll,那更是最好的。 比較難的要數這種類似于 css 選擇器的` selector,div > div.seq h2 ~ p , #id p`,如果使用從左向右的查找規則,效率很低,而從右向左,可以提高效率。 本章就來介紹 tokensize 函數,看看它是如何將復雜的 selector 處理成 tokens 的。 我們以 `div > div.seq h2 ~ p , #id p` 為例,這是一個很簡單的 css,逗號 , 將表達式分成兩部分。css 中有一些基本的符號,這里有必要強調一下,比如 , `space > + ~`: 1. div,p , 表示并列關系,所有 div 元素和 p 元素; 2. div p 空格表示后代元素,div 元素內所有的 p 元素; 3. div>p > 子元素且相差只能是一代,父元素為 div 的所有 p 元素; 4. div+p + 表示緊鄰的兄弟元素,前一個兄弟節點為 div 的所有 p 元素; 5. div~p ~ 表示兄弟元素,所有前面有兄弟元素 div 的所有 p 元素。 除此之外,還有一些 a、input 比較特殊的: 1. a[target=_blank] 選擇所有 target 為 _blank 的所有 a 元素; 2. a[title=search] 選擇所有 title 為 search 的所有 a 元素; 3. input[type=text] 選擇 type 為 text 的所有 input 元素; 4. p:nth-child(2) 選擇其為父元素第二個元素的所有 p 元素; Sizzle 都是支持這些語法的,如果我們把這一步叫做詞法分析,那么詞法分析的結果是一個什么東西呢? `div > div.seq h2 ~ p , #id p` 經過 tokensize(selector) 會返回一個數組,該數組在函數中稱為 groups。因為這個例子有一個逗號,故該數組有兩個元素,分別是 tokens[0] 和 tokens[1],代表選擇器逗號前后的兩部分。tokens 也是數組,它的每一個元素都是一個 token 對象。 一個 token 對象結構如下所示: ``` token: { value: matched, // 匹配到的字符串 type: type, //token 類型 matches: match //去除 value 的正則結果數組 } ``` Sizzle 中 type 的種類有下面幾種:ID、CLASS、TAG、ATTR、PSEUDO、CHILD、bool、needsContext,這幾種有幾種我也不知道啥意思,child 表示 nth-child、even、odd 這種子選擇器。這是針對于 matches 存在的情況,對于 matches 不存在的情況,其 type 就是 value 的 trim() 操作,后面會談到。 tokensize 函數對 selector 的處理,連空格都不放過,因為空格也屬于 type 的一種,而且還很重要,div > div.seq h2 ~ p 的處理結果: ``` tokens: [ [value:'div', type:'TAG', matches:Array[1]], [value:' > ', type:'>'], [value:'div', type:'TAG', matches:Array[1]], [value:'.seq', type:'CLASS', matches:Array[1]], [value:' ', type:' '], [value:'h2', type:'TAG', matches:Array[1]], [value:' ~ ', type:'~'], [value:'p', type:'TAG', matches:Array[1]], ] ``` 這個數組會交給 Sizzle 的下一個流程來處理,今天暫不討論。 ## tokensize 源碼 照舊,先來看一下幾個正則表達式。 ``` var rcomma = /^[\x20\t\r\n\f]*,[\x20\t\r\n\f]*/; rcomma.exec('div > div.seq h2 ~ p');//null rcomma.exec(' ,#id p');//[" ,"] ``` rcomma 這個正則,主要是用來區分 selector 是否到下一個規則,如果到下一個規則,就把之前處理好的 push 到 groups 中。這個正則中 `[\x20\t\r\n\f]` 是用來匹配類似于 whitespace 的,主體就一個逗號。 ``` var rcombinators = /^[\x20\t\r\n\f]*([>+~]|[\x20\t\r\n\f])[\x20\t\r\n\f]*/; rcombinators.exec(' > div.seq h2 ~ p'); //[" > ", ">"] rcombinators.exec(' ~ p'); //[" ~ ", "~"] rcombinators.exec(' h2 ~ p'); //[" ", " "] ``` 是不是看來 rcombinators 這個正則表達式,上面 tokens 那個數組的內容就完全可以看得懂了。 其實,如果看 jQuery 的源碼,rcomma 和 rcombinators 并不是這樣來定義的,而是用下面的方式來定義: ``` var whitespace = "[\\x20\\t\\r\\n\\f]"; var rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), ``` 有的時候必須得要佩服 jQuery 中的做法,該簡則簡,該省則省,每一處代碼都是極完美的。 還有兩個對象,Expr 和 matchExpr,Expr 是一個非常關鍵的對象,它涵蓋了幾乎所有的可能的參數,比較重要的參數比如有: ``` Expr.filter = { "TAG": function(){...}, "CLASS": function(){...}, "ATTR": function(){...}, "CHILD": function(){...}, "ID": function(){...}, "PSEUDO": function(){...} } Expr.preFilter = { "ATTR": function(){...}, "CHILD": function(){...}, "PSEUDO": function(){...} } ``` 這個 filter 和 preFilter 是處理 type=TAG 的關鍵步驟,包括一些類似于 input[type=text] 也是這幾個函數處理,也比較復雜,我本人是看迷糊了。還有 matchExpr 正則表達式: ``` var identifier = "(?:\\\\.|[\\w-]|[^\0-\\xa0])+", attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + // Operator (capture 2) "*([*^$|!~]?=)" + whitespace + // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + "*\\]", pseudos = ":(" + identifier + ")(?:\\((" + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: // 1. quoted (capture 3; capture 4 or capture 5) "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + // 2. simple (capture 6) "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + // 3. anything else (capture 2) ".*" + ")\\)|)", booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped"; var matchExpr = { "ID": new RegExp( "^#(" + identifier + ")" ), "CLASS": new RegExp( "^\\.(" + identifier + ")" ), "TAG": new RegExp( "^(" + identifier + "|[*])" ), "ATTR": new RegExp( "^" + attributes ), "PSEUDO": new RegExp( "^" + pseudos ), "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), // For use in libraries implementing .is() // We use this for POS matching in `select` "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) } ``` matchExpr 作為正則表達式對象,其 key 的每一項都是一個 type 類型,將 type 匹配到,交給后續函數處理。 tokensize 源碼如下: ``` var tokensize = function (selector, parseOnly) { var matched, match, tokens, type, soFar, groups, preFilters, cached = tokenCache[selector + " "]; // tokenCache 表示 token 緩沖,保持已經處理過的 token if (cached) { return parseOnly ? 0 : cached.slice(0); } soFar = selector; groups = []; preFilters = Expr.preFilter; while (soFar) { // 判斷一個分組是否結束 if (!matched || (match = rcomma.exec(soFar))) { if (match) { // 從字符串中刪除匹配到的 match soFar = soFar.slice(match[0].length) || soFar; } groups.push((tokens = [])); } matched = false; // 連接符 rcombinators if ((match = rcombinators.exec(soFar))) { matched = match.shift(); tokens.push({ value: matched, type: match[0].replace(rtrim, " ") }); soFar = soFar.slice(matched.length); } // 過濾,Expr.filter 和 matchExpr 都已經介紹過了 for (type in Expr.filter) { if ((match = matchExpr[type].exec(soFar)) && (!preFilters[type] || (match = preFilters[type](match)))) { matched = match.shift(); // 此時的 match 實際上是 shift() 后的剩余數組 tokens.push({ value: matched, type: type, matches: match }); soFar = soFar.slice(matched.length); } } if (!matched) { break; } } // parseOnly 這個參數應該以后會用到 return parseOnly ? soFar.length : soFar ? Sizzle.error(selector) : // 存入緩存 tokenCache(selector, groups).slice(0); } ``` 不僅數組,字符串也有 slice 操作,而且看源碼的話,jQuery 中對字符串的截取,使用的都是 slice 方法。而且本代碼中出現的 array.slice(0)方法是一個淺拷貝數組的好方法。 如果此時 parseOnly 不成立,則返回結果需從 tokenCache 這個函數中來查找: ``` var tokenCache = createCache(); function createCache() { var keys = []; function cache( key, value ) { // Expr.cacheLength = 50 if ( keys.push( key + " " ) > Expr.cacheLength ) { // 刪,最不經常使用 delete cache[ keys.shift() ]; } // 整個結果返回的是 value return (cache[ key + " " ] = value); } return cache; } ``` 可知,返回的結果是 groups,tokensize 就學完了,下章將介紹 tokensize 的后續。 ## 總結 對于一個復雜的 selector,其 tokensize 的過程遠比今天介紹的要復雜,今天的例子有點簡單(其實也比較復雜了),后面的內容更精彩。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看