本节内容
1.Sizzle.compile
2.matcherFromTokens
3.elementMatcher
4.addCombinator
先来一个例子
html代码
<!DOCTYPE>
<html>
<head>
<title></title>
</head>
<body>
<div id="text">
<p>
<input type="text" />
</p>
<div class="aaron">
<input type="checkbox" name="readme" value="Submit" />
<p>Sizzle</p>
</div>
</div>
</body>
</html>
+
原始选择器div > p + div.aaron input[type="checkbox"]
=
seed集合[input, input]
重组的选择器div > p + div.aaron [type="checkbox"]
Token序列
"="后面的是经过Sizzle.tokenize
和Sizzle.select
的处理获取到的数据
Expr.filter
Token序列中每一种type都有对应的处理方法
Expr.filter = {
ATTR : function (name, operator, check) {
CHILD : function (type, what, argument, first, last) {
CLASS : function (className) {
ID : function (id) {
PSEUDO : function (pseudo, argument) {
TAG : function (nodeNameSelector) {
}
先看看两个匹配器的源码
// ID元匹配器工厂
Expr.filter[ "ID" ] = function( id ) {
var attrId = id.replace( runescape, funescape );
// 生成一个匹配器
return function( elem ) {
var node = typeof elem.getAttributeNode !== "undefined" &&
elem.getAttributeNode( "id" );
// 判断id是否一致
return node && node.value === attrId;
};
};
// 属性元匹配器工厂
// name :属性名
// operator :操作符
// check : 要检查的值
// 例如选择器 [type="checkbox"]中,name="type" operator="=" check="checkbox"
"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 :
// 这样解释: lang*=en 匹配这样 <html lang="xxxxenxxx">的节点
operator === "*=" ? check && result.indexOf(check) > -1 :
// 如果是末尾相等,判断目标值是否在当前属性值的末尾
operator === "$=" ? check && result.slice(-check.length) === check :
// 这样解释: lang~=en 匹配这样 <html lang="zh_CN en">的节点
operator === "~=" ? (" " + result + " ").indexOf(check) > -1 :
// 这样解释: lang=|en 匹配这样 <html lang="en-US">的节点
operator === "|=" ? result === check || result.slice(0, check.length + 1) === check + "-" :
// 其他情况的操作符号表示不匹配
false;
};
},
Sizzle.compile
接下来要做得事情就是对每一个Token序列都生成一个匹配器,然后再将所有匹配器融合成一个大的匹配器
compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) {
var i,
setMatchers = [],
elementMatchers = [],
cached = compilerCache[ selector + " " ];
if ( !cached ) { // 依旧看看有没有缓存
if ( !match ) { // 如果没有词法解析过
match = tokenize( selector );
}
i = match.length;
// 如果有并联选择器这里多次循环
while ( i-- ) {
// 这里用matcherFromTokens来生成对应Token的匹配器
cached = matcherFromTokens( match[ i ] );
if ( cached[ expando ] ) {
setMatchers.push( cached );
} else { // 那些普通的匹配器都压入了elementMatchers里边
elementMatchers.push( cached );
}
}
// 这里可以看到,是通过matcherFromGroupMatchers这个函数来生成最终的匹配器
cached = compilerCache(
selector,
matcherFromGroupMatchers( elementMatchers, setMatchers )
);
cached.selector = selector;
}
// 把这个终极匹配器返回到select函数中
return cached;
};
matcherFromTokens
matcherFromTokens
才是Sizzle.compile
中起主要作用的方法
// 生成用于匹配单个选择器组的函数
// 充当了selector“tokens”与Expr中定义的匹配方法的串联与纽带的作用
// Sizzle没有直接将拿到的“分词”结果与Expr中的方法逐个匹配
// 而是先根据规则组合出一个大的匹配方法
function matcherFromTokens( tokens ) {
var checkContext, matcher, j,
len = tokens.length,
leadingRelative = Expr.relative[ tokens[ 0 ].type ],
implicitRelative = leadingRelative || Expr.relative[ " " ],
i = leadingRelative ? 1 : 0,
// 这一大段代码是用来确保elem能够在context中找到
// 以下两个函数中的checkContext都等同于context
// matchContext:逐层查找elem的parentNode,看看parentNode是否等于context
matchContext = addCombinator( function( elem ) {
return elem === checkContext;
}, implicitRelative, true ),
// matchAnyContext:用于查找列表(checkContext)中是否包含元素(elem)
matchAnyContext = addCombinator( function( elem ) {
return indexOf( checkContext, elem ) > -1;
}, implicitRelative, true ),
// 在生成匹配器之前matchers就已经压入了函数
matchers = [ function( elem, context, xml ) {
var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
// 之前说checkContext等同于context的原因
// 判断context.nodeType
( checkContext = context ).nodeType ?
// 如果有context是一个节点
matchContext( elem, context, xml ) :
// 如果有context是一个集合
matchAnyContext( elem, context, xml ) );
checkContext = null;
return ret;
} ];
// 正片开始
for ( ; i < len; i++ ) {
// "空 > ~ +"
if ( ( matcher = Expr.relative[ tokens[ i ].type ] ) ) { // 当遇到关系选择器时elementMatcher函数将matchers数组中的函数生成一个函数
matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ];
} else { // ATTR CHILD CLASS ID PSEUDO TAG
// 生成对应的匹配器
matcher = Expr.filter[ tokens[ i ].type ].apply( null, tokens[ i ].matches );
if ( matcher[ expando ] ) {
j = ++i;
for ( ; j < len; j++ ) {
if ( Expr.relative[ tokens[ j ].type ] ) {
break;
}
}
return setMatcher(
i > 1 && elementMatcher( matchers ),
i > 1 && toSelector(
tokens
.slice( 0, i - 1 )
.concat( { value: tokens[ i - 2 ].type === " " ? "*" : "" } )
).replace( rtrim, "$1" ),
matcher,
i < j && matcherFromTokens( tokens.slice( i, j ) ),
j < len && matcherFromTokens( ( tokens = tokens.slice( j ) ) ),
j < len && toSelector( tokens )
);
}
// 将匹配器压入matchers中
matchers.push( matcher );
}
}
// 将matchers中的函数合成1个函数
return elementMatcher( matchers );
}
简单来说就是遇到一般类型的匹配器就压入matchers,遇到关系型匹配器(空 > ~ +)就合并matchers中的函数
elementMatcher
将matchers中的函数合并成一个。无非就是将matchers中的函数拿出来一个个执行,全部执行结果为true才返回true,否则返回false
function elementMatcher( matchers ) {
return matchers.length > 1 ?
// 多个匹配器
function( elem, context, xml ) {
var i = matchers.length;
// 从右到左开始匹配
while ( i-- ) {
if ( !matchers[ i ]( elem, context, xml ) ) {
return false;
}
}
return true;
} :
// 单个匹配器
matchers[ 0 ];
}
addCombinator
addCombinator方法就是为了生成有位置词素的匹配器,简单来说就是将elem元素转移成elem.parentNode或者previousSibling再注入到匹配器中。
function addCombinator( matcher, combinator, base ) {
var dir = combinator.dir,
skip = combinator.next,
key = skip || dir,
checkNonElements = base && key === "parentNode",
doneName = done++; // 第几个关系选择器
return combinator.first ?
// 如果是紧密关系的位置词素
function( elem, context, xml ) {
while ( ( elem = elem[ dir ] ) ) {
if ( elem.nodeType === 1 || checkNonElements ) {
// 找到第一个亲密的节点,立马就用终极匹配器判断这个节点是否符合前面的规则
// 也就循环一次
return matcher( elem, context, xml );
}
}
return false;
} :
// 如果是不紧密关系的位置词素
function( elem, context, xml ) {
var oldCache, uniqueCache, outerCache,
newCache = [ dirruns, doneName ];
// We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching
if ( xml ) {
while ( ( elem = elem[ dir ] ) ) {
if ( elem.nodeType === 1 || checkNonElements ) {
if ( matcher( elem, context, xml ) ) {
return true;
}
}
}
} else {
while ( ( elem = elem[ dir ] ) ) {
if ( elem.nodeType === 1 || checkNonElements ) {
outerCache = elem[ expando ] || ( elem[ expando ] = {} );
// Support: IE <9 only
// Defend against cloned attroperties (jQuery gh-1709)
uniqueCache = outerCache[ elem.uniqueID ] ||
( outerCache[ elem.uniqueID ] = {} );
if ( skip && skip === elem.nodeName.toLowerCase() ) {
elem = elem[ dir ] || elem;
} else if ( ( oldCache = uniqueCache[ key ] ) &&
oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) {
// Assign to newCache so results back-propagate to previous elements
return ( newCache[ 2 ] = oldCache[ 2 ] );
} else {
// Reuse newcache so results back-propagate to previous elements
uniqueCache[ key ] = newCache;
// 主要的执行部分在这里
// 如果不是紧密关系,就一级一级的去匹配
if ( ( newCache[ 2 ] = matcher( elem, context, xml ) ) ) {
return true;
}
}
}
}
}
return false;
};
}
网友评论