Sizzle源碼分析:二 詞法分析

上一篇咱們瞭解了Sizzle的總體流程,下面我開始一點點分析各個流程,咱們進行查詢的第一步就是詞法分析tokenize,一樣先了解下思路,若是是#div_test > span input[checked=true]會發生什麼:數組


一個字符串的每一個節點都被分析爲如下數據結構:{type:'對應的Token類型',value:'匹配到的字符串',  matches:'正則匹配到的一個結構'}瀏覽器

type包括有TAG, ID, CLASS, ATTR, CHILD, PSEUDO, NAME,表示每一個字符串的類型緩存

value是指字符串自己的值數據結構

match正則匹配到的一個結構async

咱們經過console打印出來的數據結構是下面:函數

 

首先說明一下下面代碼中tokens數組和groups數組的關係,oop

好比#div_test span 那麼咱們分析後的結果是一個tokens數組,包含兩個元素div_test和span [{type:"ID",value:"div_test"},{type:"TAG",value:"span"}]ui

若是是 #div_test span,#sp_test span,那麼是兩組tokens數組 一個包含div_test和span 一個包含sp_test和span 那這兩組tokens就造成一個二維數組groupsspa

[code

  [{type:"ID",value:"div_test"},{type:"TAG",value:"span"}]

  [{type:"ID",value:"sp_test "},{type:"TAG",value:"span"}]

]

代碼整體思路是

1. 若是有逗號,會過濾掉這個逗號,好比"div1,div2"第二次循環是selector的值是",div2"須要刪掉前面的逗號,而後爲groups新增元素

2. 若是是關係運算符 > + 空格 ~開頭,直接壓入數組

3. 而後開始分析 ID,TAG,CLASS,ATTR,CHILD,PSEUDO選擇符,若是匹配到了相關選擇符,再看看是否須要預處理,若是須要再進行預處理返回(只有部分選擇符須要,後面詳解),而後壓入數組,刪除相關選擇符字符串

4. 繼續下一個循環直到結束

//把字符串轉換爲token數組,格式爲{type:'對應的Token類型',value:'匹配到的字符串',  matches:'正則匹配到的一個結構'}
function
tokenize(selector, parseOnly) { var matched, match, tokens, type, soFar, groups, preFilters, cached = tokenCache[selector + " "]; if (cached) {//若是有緩存直接讀取緩存 return parseOnly ? 0 : cached.slice(0); } soFar = selector; groups = []; //這是最後要返回的二維數組

     //預處理器,對token進行預處理
//預處理,有的選擇器,好比屬性選擇器與僞類從選擇器組分割出來,還要再細分
//屬性選擇器要切成屬性名,屬性值,操做符;僞類要切爲類型與傳參;
//子元素過濾僞類還要根據an+b的形式再劃分
preFilters = Expr.preFilter;
        while (soFar) {//對選擇符逐個字符分析
       //若是第一個字符是逗號,跳過逗號,而且壓入第一個空token分組,groups是個二維數組,每一個元素表明一個token數組, if (!matched || (match = rcomma.exec(soFar))) { if (match) { soFar = soFar.slice(match[0].length) || soFar; } groups.push(tokens = []); } matched = false; //若是開頭的字符是關係選擇符 > + 空格 ~ 將他直接壓入tokens數組,而且刪除selector相關部分 if ((match = rcombinators.exec(soFar))) { matched = match.shift(); tokens.push({ value: matched, // Cast descendant combinators to space type: match[0].replace(rtrim, " ") }); soFar = soFar.slice(matched.length); }        /*而後開始分析ID,TAG,CLASS,ATTR,CHILD,PSEUDO
        matchExpr 過濾正則
       ATTR: /^\[[\x20\t\r\n\f]*((?:\\.|[\w-]|[^\x00-\xa0])+)[\x20\t\r\n\f]*(?:([*^$|!~]?=)[\x20\t\r\n\f]*(?:(['"])((?:\\.|[^\\])*?)\3|((?:\\.|[\w#-]|[^\x00-\xa0])+)|)|)[\x20\t\r\n\f]*\]/
       CHILD: /^:(only|first|last|nth|nth-last)-(child|of-type)(?:\([\x20\t\r\n\f]*(even|odd|(([+-]|)(\d*)n|)[\x20\t\r\n\f]*(?:([+-]|)[\x20\t\r\n\f]*(\d+)|))[\x20\t\r\n\f]*\)|)/i
       CLASS: /^\.((?:\\.|[\w-]|[^\x00-\xa0])+)/

        ID: /^#((?:\\.|[\w-]|[^\x00-\xa0])+)/
        PSEUDO: /^:((?:\\.|[\w-]|[^\x00-\xa0])+)(?:\(((['"])((?:\\.|[^\\])*?)\3|((?:\\.|[^\\()[\]]|\[[\x20\t\r\n\f]*((?:\\.|[\w-]|[^\x00-\xa0])+)[\x20\t\r\n\f]*(?:([*^$|!~]?=)[\x20\t\r\n\f]*(?:(['"])((?:\\.|[^\\])*?)\8|((?:\\.|[\w#-]|[^\x00-\xa0])+)|)|)[\x20\t\r\n\f]*\])*)|.*)\)|)/
        TAG: /^((?:\\.|[\w*-]|[^\x00-\xa0])+)/
        bool: /^(?:checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped)$/i
        needsContext: /^[\x20\t\r\n\f]*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\([\x20\t\r\n\f]*((?:-\d)?\d*)[\x20\t\r\n\f]*\)|)(?=[^-]|$)/i

        */

            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 the length of the invalid excess
        // if we're just parsing
        // Otherwise, throw an error or return tokens
        return parseOnly ?
            soFar.length :
            soFar ?
            Sizzle.error(selector) :
        // Cache the tokens
        tokenCache(selector, groups).slice(0);
    }

 

這裏判斷選擇符的過程就是經過遍歷Expr.filter來判斷,咱們來看看這個東西:

除了這5個,後面還根據瀏覽器兼容性新增了ID類型,爲什麼要遍歷這個對象呢,由於Sizzle裏面把選擇器字符串的類型就分了這麼幾種

ID:ID選擇符

Class:類選擇符

Tag:標籤選擇符

ATTR:屬性標籤

CHILD:包括(only|first|last|nth|nth-last)-(child|of-type)等等對子類的標籤

PSEUDO:其餘僞類選擇符

對這些類型進行正則匹配以後,token數組就基本創建起來了,整個詞法分析過程也就完成了。

順便介紹下toSelector函數,他的過程恰好相反,就是把tokens字符串裏面的值還原爲字符串形式。

       function toSelector( tokens ) {
            var i = 0,
                len = tokens.length,
                selector = "";
            for ( ; i < len; i++ ) {
                selector += tokens[i].value;
            }
            return selector;
        }
相關文章
相關標籤/搜索