jQuery原始碼分析之tokenize方法的Expr.preFilter

antzone發表於2017-04-03

關於tokenize()方法的分析可以參閱jQuery原始碼分析之tokenize()方法一章節。

下面再分析一下此方法的Expr.preFilter,需要的朋友可以做一下參考。

程式碼例項如下:

[JavaScript] 純文字檢視 複製程式碼
Expr.preFilter : {
    "ATTR" : function(match) {
        /*
         * 完成如下任務:
         * 1、屬性名稱解碼
         * 2、屬性值解碼
         * 3、若判斷符為~=,則在屬性值兩邊加上空格
         * 4、返回最終的mtach物件
         * 
         * match[1]表示屬性名稱,
         * match[1].replace(runescape, funescape):將屬性名稱中的十六進位制數解碼成
         *   單位元組unicode字元或雙位元組unicode字元(中文或其它需要兩個位元組表達的文字)
         * 正規表示式的詳細說明,可以參看我的“詳解jQuery選擇器正規表示式”文章
         */
        match[1] = match[1].replace(runescape, funescape);
  
        /*
         * 將屬性值解碼
         * match[4]:表示放在單引號或雙引號內的屬性值
         * match[5]: 表示不用引號括起來的屬性值
         */
        match[3] = (match[4] || match[5] || "").replace(runescape,
                funescape);
  
        /*
         * ~=的意思是單詞匹配,在W3C中對單詞的定義是以空白為不同單詞的分隔符
         * 故此處在match[3]兩邊加上空格後,可以利用indexOf,正確識別出該單詞是否存在
         */
        if (match[2] === "~=") {
            match[3] = " " + match[3] + " ";
        }
  
        /*
         * 返回有用的前四個元素結果
         */
        return match.slice(0, 4);
    },
  
    "CHILD" : function(match) {
        /*
         * 完成如下幾項任務:
         * 1、把命令中child和of-type之前的字元變成小寫字元
         * 2、對於nth開頭的選擇器檢查括號內的資料有效性
         * 3、match[4]和match[5]分別存放xn+b中的x和b,x和b允許是負數
         * 4、返回最終的match物件
         * 
         * match[1]:(only|first|last|nth|nth-last)中的一個
         */
        match[1] = match[1].toLowerCase();
  
        /*
         * 對於nth-child、nth-of-type、nth-last-child、nth-last-of-type四種型別括號內需設定有效資料
         * 而其它則括號內不允許有任何資料
         */
        if (match[1].slice(0, 3) === "nth") {
            /*
             * 若選擇器括號內沒有有效引數,則丟擲異常
             * 舉例:若選擇器是nth或nth(abc)則屬於非法選擇器
             */
            if (!match[3]) {
                Sizzle.error(match[0]);
            }
            /*
             * 下面先以nth-child()為例介紹一下語法,以便更好的理解下面程式碼的作用
             * nth-child允許的幾種使用方式如下:
             *   :nth-child(even)
             *   :nth-child(odd)
             *   :nth-child(3n)
             *   :nth-child(+2n+1)
             *   :nth-child(2n-1)
             * 下面程式碼中賦值號左側的match[4]、match[5]用於分別記錄括號內n前及n後的數值,包括正負號
             * 對於:nth-child(even)和:nth-child(odd)來說,match[4]為空,
             *   所以返回 2 * (match[3] === "even" || match[3] === "odd")的計算結果
             *   因為在js中true=1,false=0,所以(match[3] === "even" || match[3] === "odd")等於1
             *   因此,2 * (match[3] === "even" || match[3] === "odd")的計算結果為2
             * 
             * 等號右側的“+”的作用是強制型別轉換,將之後的字串轉換成數值型別 
             */
            match[4] = +(match[4] ? match[5] + (match[6] || 1)
                    : 2 * (match[3] === "even" || match[3] === "odd"));
            match[5] = +((match[7] + match[8]) || match[3] === "odd");
  
        } else if (match[3]) {
            /*
             * 若非nth起頭的其它CHILD型別選擇器帶有括號說明,則丟擲異常
             * 這裡jQuery並沒有嚴格按照W3C的規則來判定,因為其允許:first-child()的這種形式存在
             * 也就是對於jQuery來說:first-child()等同於:first-child,是合法選擇器
             */
            Sizzle.error(match[0]);
        }
  
        return match;
    },
  
    "PSEUDO" : function(match) {
        /*
         * 完成如下任務:
         * 1、獲取偽類中用引號括起來的值
         * 2、對於非引號括起來的值,若存在偽類巢狀,則進一步解析確定當前偽類實際結束位置,
         *  獲取當前偽類的完整字串和值
         * 3、返回match中的前三項的副本。
         * 
         * unquoted表示括號內非引號括起來的值,
         * 以:eq(2)為例,unquoted=2
         */
        var excess, unquoted = !match[5] && match[2];
  
        /*
         * 因為pseudo與child的匹配正規表示式有交集,所以,需要把屬於child的部分忽略掉
         */
        if (matchExpr["CHILD"].test(match[0])) {
            return null;
        }
        /*
         * 若括號內的值使用引號(match[3])括起來的,
         * 則將除引號外的值(match[4])賦給match[2]。
         * match[3]表示引號。
         */
        if (match[3] && match[4] !== undefined) {
            match[2] = match[4];
        } else if (unquoted
                /*
                 * rpseudo.test(unquoted):用來測試unquoted是否包含偽類,
                 * 若包含偽類,則說明有可能存在偽類巢狀的可能性,需要進一步對unquoted進行解析
                 * 例如: :not(:eq(3))
                 */
                && rpseudo.test(unquoted)
                &&
                /*
                 * 獲取unquoted中連續有效地選擇器最後一個字元所在位置
                 */
                (excess = tokenize(unquoted, true))
                &&
                /*
                 * unquoted.indexOf(")", unquoted.length - excess)
                 *   從之前獲得的連續有效地選擇器最後一個字元所在位置之後找到")"所在位置,
                 *   通常就在當前位置之後。
                 * 再減去unquoted.length,用來獲得match[0]中的有效完整的偽類字串最後位置,
                 *   注意,此時excess是一個負值
                 * 
                 */
                (excess = unquoted.indexOf(")", unquoted.length
                        - excess)
                        - unquoted.length)) {
  
            // 獲取有效的完整偽類match[0]和偽類括號內的資料match[2]
            match[0] = match[0].slice(0, excess);
            match[2] = unquoted.slice(0, excess);
        }
  
        // 返回match前三個元素的副本
        return match.slice(0, 3);
    }
}

相關文章