Sizzle 源码分析 (二)

在Sizzle函数中,如果能快速处理或者通过querySelector处理,那么就使用它处理。否则使用select函数处理 。

select函数

  select = Sizzle.select = function (selector, context, results, seed) {
    var i, tokens, token, type, find,
    // 判定是否是 pre-compiled 的选择器
      compiled = typeof selector === "function" && selector,
      // 这里由于compiled 是false ,所以先认为selector 是字符串,进入tokenize函数 ,进入词法分析 。
      // 将 selector 分为组并返回
      match = !seed && tokenize((selector = compiled.selector || selector));
  };

所以,这一节的主要内容是 tokenize 函数

tokenize 函数

  tokenize = Sizzle.tokenize = function (selector, parseOnly) {
    var matched, match, tokens, type,
      soFar, groups, preFilters,
      // 先查看是否有缓存
      cached = tokenCache[selector + " "];

    if (cached) {
        // 如果有缓存,就先从缓冲中取 。
      return parseOnly ? 0 : cached.slice(0);
    }

    
    soFar = selector; // 下面对选择器从左至右进行分析
    groups = [];      // 用 , 分割的组合选择器,每个选择器都是一个组 。
    preFilters = Expr.preFilter; // 过滤器

    while (soFar) {

      // 第一个运行matched 为undefined,一定为假 。
      // 105行,找出逗号  rcomma = new RegExp("^" + whitespace + "*," + whitespace + "*"), 
      // 逗号用来分组,所以下面if 的逻辑主要是添加组 ,即group 。
      if (!matched || (match = rcomma.exec(soFar))) {
        if (match) {
          soFar = soFar.slice(match[0].length) || soFar;
        }
        // 将一个数组push到组中 。
        groups.push((tokens = []));
      }

      matched = false;


    // 106 行 rcombinators = new RegExp("^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*"),
    // 这个正则表达式就是为了找出 关系符 ">+~ " 。
      // 在上面添加了组,把逗号已经去掉,下面就是逗号之后的标识符 ,首先 match = rcombinators.exec(soFar)) 判定关系符号,但是第一次从组跳下,这里肯定为false 。所以又跳转到之后的if  。
      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 ,class 还是 tag 。

      //  identifier = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", 这里的 \\\\. 表示 在css中,可以有转义字符作为标识符 。比如 \$,\&
      // 捕捉属性选择器,这个正则是最难的,不一定完全理解。
      //    attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace +
      //"*([*^$|!~]?=)" + whitespace +
      //"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace +
      //"*\\]",

      //     booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",

      // 处理各种伪类 。
      //     pseudos = ":(" + identifier + ")(?:\\((" +
      //"('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" +
      //"((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" +
      //".*" +
      //")\\)|)",

      //     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"),
      // "needsContext": new RegExp("^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +
      //  whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i")
      // },

      for (type in Expr.filter) {
          // 如果是上面的一种 、
          // preFilters是用于分析选择器的名字与参数
          // 预处理,有的选择器,比如属性选择器与伪类从选择器组分割出来,还要再细分
          // 属性选择器要切成属性名,属性值,操作符;伪类要切为类型与传参;
          // 子元素过滤伪类还要根据an+b的形式再划分
        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;
      }
    }

    // 正常情况下,soFar全部解析完毕,此时为空字符串 。如果仅仅如parse,那么返回剩下长度,否则,抛出异常 。
    return parseOnly ?
      soFar.length :
      soFar ?
        Sizzle.error(selector) :
        // 缓存起来 。
        tokenCache(selector, groups).slice(0);
  };

filter 部分


    // 这是filter,返回match的柯里化函数 。在编译部分会使用,这里不会用到 。
    
    filter: {
        // 标签过滤器 ,返回一个柯里化函数 。
        // 验证元素的名称是否就是当前传入的Tag 。Tag放入闭包中 。
      "TAG": function (nodeNameSelector) {
        var nodeName = nodeNameSelector.replace(runescape, funescape).toLowerCase();
        return nodeNameSelector === "*" ?
          function () { return true; } :
          function (elem) {
            return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
          };
      },

       // 类过滤器 ,返回一个柯里化函数 。
       // 验证元素的类名称是否包含当前传入的className 。
      "CLASS": function (className) {
        var pattern = classCache[className + " "];

        return pattern ||
          (pattern = new RegExp("(^|" + whitespace + ")" + className + "(" + whitespace + "|$)")) &&
          classCache(className, function (elem) {
            return pattern.test(typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || "");
          });
      },

      "ATTR": function (name, operator, check) {
          // 返回的是函数 !
        return function (elem) {
          var result = Sizzle.attr(elem, name);
          // 如果属性值为空 
          if (result == null) {
            return operator === "!=";
          }
          // 如果操作符为空,
          if (!operator) {
            return true;
          }
          // 将属性值转化为字符串。
          result += "";

          return operator === "=" ? result === check :
            operator === "!=" ? result !== check :
              operator === "^=" ? check && result.indexOf(check) === 0 :
                operator === "*=" ? check && result.indexOf(check) > -1 :
                  operator === "$=" ? check && result.slice(-check.length) === check :
                    operator === "~=" ? (" " + result.replace(rwhitespace, " ") + " ").indexOf(check) > -1 :
                      operator === "|=" ? result === check || result.slice(0, check.length + 1) === check + "-" :
                        false;
        };
      },

      // 这里处理子元素过滤伪类,如:nth-child, :first-child, :only-child
      "CHILD": function (type, what, argument, first, last) {
        var simple = type.slice(0, 3) !== "nth",
          forward = type.slice(-4) !== "last",
          ofType = what === "of-type";

        return first === 1 && last === 0 ?

          // Shortcut for :nth-*(n)
          function (elem) {
            return !!elem.parentNode;
          } :

          function (elem, context, xml) {
            var cache, outerCache, node, diff, nodeIndex, start,
              dir = simple !== forward ? "nextSibling" : "previousSibling",
              parent = elem.parentNode,
              name = ofType && elem.nodeName.toLowerCase(),
              useCache = !xml && !ofType;

            if (parent) {

              // :(first|last|only)-(child|of-type)
              if (simple) {
                while (dir) {
                  node = elem;
                  while ((node = node[dir])) {
                    if (ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1) {
                      return false;
                    }
                  }
                  // Reverse direction for :only-* (if we haven't yet done so)
                  start = dir = type === "only" && !start && "nextSibling";
                }
                return true;
              }

              start = [forward ? parent.firstChild : parent.lastChild];

              // non-xml :nth-child(...) stores cache data on `parent`
              if (forward && useCache) {
                // Seek `elem` from a previously-cached index
                outerCache = parent[expando] || (parent[expando] = {});
                cache = outerCache[type] || [];
                nodeIndex = cache[0] === dirruns && cache[1];
                diff = cache[0] === dirruns && cache[2];
                node = nodeIndex && parent.childNodes[nodeIndex];

                while ((node = ++nodeIndex && node && node[dir] ||

                  // Fallback to seeking `elem` from the start
                  (diff = nodeIndex = 0) || start.pop())) {

                  // When found, cache indexes on `parent` and break
                  if (node.nodeType === 1 && ++diff && node === elem) {
                    outerCache[type] = [dirruns, nodeIndex, diff];
                    break;
                  }
                }

                // Use previously-cached element index if available
              } else if (useCache && (cache = (elem[expando] || (elem[expando] = {}))[type]) && cache[0] === dirruns) {
                diff = cache[1];

                // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...)
              } else {
                // Use the same loop as above to seek `elem` from the start
                while ((node = ++nodeIndex && node && node[dir] ||
                  (diff = nodeIndex = 0) || start.pop())) {

                  if ((ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1) && ++diff) {
                    // Cache the index of each encountered element
                    if (useCache) {
                      (node[expando] || (node[expando] = {}))[type] = [dirruns, diff];
                    }

                    if (node === elem) {
                      break;
                    }
                  }
                }
              }

              // Incorporate the offset, then check against cycle size
              diff -= last;
              return diff === first || (diff % first === 0 && diff / first >= 0);
            }
          };
      },

      "PSEUDO": function (pseudo, argument) {
        // pseudo-class names are case-insensitive
        // http://www.w3.org/TR/selectors/#pseudo-classes
        // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
        // Remember that setFilters inherits from pseudos
        var args,
          fn = Expr.pseudos[pseudo] || Expr.setFilters[pseudo.toLowerCase()] ||
            Sizzle.error("unsupported pseudo: " + pseudo);

        // The user may use createPseudo to indicate that
        // arguments are needed to create the filter function
        // just as Sizzle does
        if (fn[expando]) {
          return fn(argument);
        }

        // But maintain support for old signatures
        if (fn.length > 1) {
          args = [pseudo, pseudo, "", argument];
          return Expr.setFilters.hasOwnProperty(pseudo.toLowerCase()) ?
            markFunction(function (seed, matches) {
              var idx,
                matched = fn(seed, argument),
                i = matched.length;
              while (i--) {
                idx = indexOf(seed, matched[i]);
                seed[idx] = !(matches[idx] = matched[i]);
              }
            }) :
            function (elem) {
              return fn(elem, 0, args);
            };
        }

        return fn;
      }
    },

priFilter

    preFilter: {
      "ATTR": function (match) {
        match[1] = match[1].replace(runescape, funescape);

        // Move the given value to match[3] whether quoted or unquoted
        match[3] = (match[3] || match[4] || match[5] || "").replace(runescape, funescape);

        if (match[2] === "~=") {
          match[3] = " " + match[3] + " ";
        }

        return match.slice(0, 4);
      },

      "CHILD": function (match) {
        /* matches from matchExpr["CHILD"]
          1 type (only|nth|...)
          2 what (child|of-type)
          3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
          4 xn-component of xn+y argument ([+-]?\d*n|)
          5 sign of xn-component
          6 x of xn-component
          7 sign of y-component
          8 y of y-component
        */
        //将它的伪类名称与传参拆分为更细的单元,以数组形式返回
        //比如 ":nth-child(even)"变为
        //["nth","child","even", 2, 0, undefined, undefined, undefined]
        match[1] = match[1].toLowerCase();

        if (match[1].slice(0, 3) === "nth") {
          // nth-* requires argument
          if (!match[3]) {
            Sizzle.error(match[0]);
          }

          // numeric x and y parameters for Expr.filter.CHILD
          // remember that false/true cast respectively to 0/1
          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");

          // other types prohibit arguments
        } else if (match[3]) {
          Sizzle.error(match[0]);
        }

        return match;
      },

      "PSEUDO": function (match) {
                        //将它的伪类名称与传参进行再处理
                //比如:contains伪类会去掉两边的引号,反义伪类括号部分会再次提取
        var excess,
          unquoted = !match[6] && match[2];

        if (matchExpr["CHILD"].test(match[0])) {
          return null;
        }

        // Accept quoted arguments as-is
        if (match[3]) {
          match[2] = match[4] || match[5] || "";

          // Strip excess characters from unquoted arguments
        } else if (unquoted && rpseudo.test(unquoted) &&
          // Get excess from tokenize (recursively)
          (excess = tokenize(unquoted, true)) &&
          // advance to the next closing parenthesis
          (excess = unquoted.indexOf(")", unquoted.length - excess) - unquoted.length)) {

          // excess is a negative index
          match[0] = match[0].slice(0, excess);
          match[2] = unquoted.slice(0, excess);
        }

        // Return only captures needed by the pseudo filter method (type and argument)
        return match.slice(0, 3);
      }
    },
posted @ 2017-06-09 17:04  小精灵儿Pawn  Views(323)  Comments(0Edit  收藏  举报

如果您觉得此文有帮助,可以打赏点钱给我支付宝。 谢谢 -):