zoukankan      html  css  js  c++  java
  • 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);
          }
        },
    
  • 相关阅读:
    内存表id,name解决方案,举例(workspaces表)
    建立mysql远程访问账号
    mysql主从设定笔记
    mysql安装
    SAMBA 让Unix与Windows轻松共享 (2)
    /rc.d/rc.mysqld举例
    HTML编码规范1.0
    创建mysql存储过程
    《Linux企业应用案例精解》样章
    欢迎参加51CTO的技术门诊《OSSIM,企业信息安全管理利器》讨论
  • 原文地址:https://www.cnblogs.com/likeFlyingFish/p/6972794.html
Copyright © 2011-2022 走看看