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