在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);
}
},

Sizzle 源码分析 (二)的更多相关文章

  1. Fresco 源码分析(二) Fresco客户端与服务端交互(1) 解决遗留的Q1问题

    4.2 Fresco客户端与服务端的交互(一) 解决Q1问题 从这篇博客开始,我们开始讨论客户端与服务端是如何交互的,这个交互的入口,我们从Q1问题入手(博客按照这样的问题入手,是因为当时我也是从这里 ...

  2. Sizzle源码分析 (一)

    Sizzle 源码分析 (一) 2.1 稳定 版本 Sizzle 选择器引擎博大精深,下面开始阅读它的源代码,并从中做出标记 .先从入口开始,之后慢慢切入 . 入口函数 Sizzle () 源码 19 ...

  3. 框架-springmvc源码分析(二)

    框架-springmvc源码分析(二) 参考: http://www.cnblogs.com/leftthen/p/5207787.html http://www.cnblogs.com/leftth ...

  4. Tomcat源码分析二:先看看Tomcat的整体架构

    Tomcat源码分析二:先看看Tomcat的整体架构 Tomcat架构图 我们先来看一张比较经典的Tomcat架构图: 从这张图中,我们可以看出Tomcat中含有Server.Service.Conn ...

  5. 十、Spring之BeanFactory源码分析(二)

    Spring之BeanFactory源码分析(二) 前言 在前面我们简单的分析了BeanFactory的结构,ListableBeanFactory,HierarchicalBeanFactory,A ...

  6. Vue源码分析(二) : Vue实例挂载

    Vue源码分析(二) : Vue实例挂载 author: @TiffanysBear 实例挂载主要是 $mount 方法的实现,在 src/platforms/web/entry-runtime-wi ...

  7. 多线程之美8一 AbstractQueuedSynchronizer源码分析<二>

    目录 AQS的源码分析 该篇主要分析AQS的ConditionObject,是AQS的内部类,实现等待通知机制. 1.条件队列 条件队列与AQS中的同步队列有所不同,结构图如下: 两者区别: 1.链表 ...

  8. Sizzle源码分析:一 设计思路

    一.前言 DOM选择器(Sizzle)是jQuery框架中非常重要的一部分,在H5还没有流行起来的时候,jQuery为我们提供了一个简洁,方便,高效的DOM操作模式,成为那个时代的经典.虽然现在Vue ...

  9. ABP源码分析二:ABP中配置的注册和初始化

    一般来说,ASP.NET Web应用程序的第一个执行的方法是Global.asax下定义的Start方法.执行这个方法前HttpApplication 实例必须存在,也就是说其构造函数的执行必然是完成 ...

  10. spring源码分析(二)Aop

    创建日期:2016.08.19 修改日期:2016.08.20-2016.08.21 交流QQ:992591601 参考资料:<spring源码深度解析>.<spring技术内幕&g ...

随机推荐

  1. python基本数据类型——tuple

    一.元组的创建与转换: ages = (11, 22, 33, 44, 55) ages = tuple((11, 22, 33, 44, 55))ages = tuple([]) # 字符串.列表. ...

  2. mySql 安装教程

    看了好久别人的文章,今天就开始自己写第一篇.希望给别人能提供帮助,也可以方便自己查阅. 前两天自己安装了mysql,感觉是比oracle好装多了. mysql安装有两种方式,一种是安装包安装方式,一种 ...

  3. 树型权限管理插件:jQuery Tree Multiselect详细使用指南

    1.认识jQuery Tree Multiselect 这个插件允许用户以树型的形式来呈现列表复选框的选择.多用于权限管理中用于分配不同的权限.使用文档,请参考:     https://github ...

  4. NodeJS+Express+MongoDB 简单实现数据录入及回显展示【适合新人刚接触学习】

    近期在看NodeJS相关 不得不说NodeJS+Express 进行网站开发是很不错,对于喜欢玩JS的来说真是很好的一种Web开发组合 在接触NodeJS时受平时Java或者C#中API接口等开发的思 ...

  5. 让Unity的Inspector面板支持字符限制(restrict)功能

    今天在优化红点组件,笔者打算将红点id由10进制改为16进制处理,就打算将红点id字段由uint类型改成string类型,用于填写16进制的字符(因为在Inspector面板里,uint/int类型字 ...

  6. centOS的命令行与图形页面之间的转换

    .命令行 -> 图形界面 注意:在安装CentOS7的时候要添加GUI图形界面,否则没有效果. # startx

  7. Java学习笔记——设计模式之三.装饰模式

    函数应该做一件事,做好这件事,只做这一件事. --Clean Code 装饰模式,上代码: 先定义零件类: package cn.no3.decorator.template; public abst ...

  8. SSO单点登录的研究

    一.单点登录的概述       单点登录(Single Sign On),简称为 SSO,SSO的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统. 用以解决同一公司不同子产 ...

  9. Windows下安装Nodejs步骤

      最近打算把我们的微信端用Vue.js重构,为什么选择Vue.js,一是之前使用的是传统的asp.net mvc,多页面应用用户体验比单页面要差.二是使用过Angular.js,感觉对开发人员要求较 ...

  10. php 关于经纬度距离计算方法 成功版

    1.PHP实现通过经纬度计算距离 单位为公里 function getdistance($lng1,$lat1,$lng2,$lat2)//根据经纬度计算距离 { //将角度转为狐度  $radLat ...