分析sizzle源码并不是为了去钻牛角尖,而是去了解它的思想,学习下期中一些技术的运用。
1,sizzle中的正则表达式
jquery源码中充斥着各种正则表达式,能否看懂其源码的关键之一就是对正则表达式的理解。
RegExp对象的exec方法:返回一个数组,第一个是匹配项,后边依次是分组匹配项,如果分组匹配不成功,则为undefined。而且还返回属性index(匹配项起始index),input(要验证的字符串)
比如:
var r=/^(\d{1})\s((\w?)|(\d{1}))$/.exec("2 ")
r=["2 ", "2", "", "", undefined];
r.index=0
r.input="2 "
了解了这个方法后,来具体分析下期中的正则表达式。
匹配空白 水平制表符 回车 换行 换页
whitespace = "[\\x20\\t\\r\\n\\f]"
说明:对于字符串需要双重转义\。\x20是空白符,\t水平制表符,\r回车,\n换行,\f换页
characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+"
说明:?: 非捕获性分组,不会创建反向引用的分组,即不会放在exec结果集中。\\.匹配\加任意字符;[\w-]匹配[a-zA-Z0-9_]或-;[^\\x00-\\xa0]表示ISO 10646 characters 161 and higher
attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace +"*(?:([*^$|!~]?=)" + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]"
说明:\3反向引用第三个匹配项。此正则用来匹配属性 例如:[a="b"] [a=b] [a|='er']
rtrim=/^[\x20\t\r\n\f]+|((?:^|[^\\])(?:\\.)*)[\x20\t\r\n\f]+$/g
说明:匹配首位字符串
rcomma=/^[\x20\t\r\n\f]*,[\x20\t\r\n\f]*/
说明:匹配是否为逗号分隔
rcombinators=/^[\x20\t\r\n\f]*([>+~]|[\x20\t\r\n\f])[\x20\t\r\n\f]*/
说明:匹配是否是关系符
rsibling=/[\x20\t\r\n\f]*[+~]/
说明:兄弟节点标示符
rattributeQuotes=/=[\x20\t\r\n\f]*([^\]'"]*)[\x20\t\r\n\f]*\]/g
说明:属性的value
rpseudo=/:((?:\\.|[\w-]|[^\x00-\xa0])+)(?:\(((['"])((?:\\.|[^\\])*?)\3|((?:\\.|[^\\()[\]]|\[[\x20\t\r\n\f]*((?:\\.|[\w-]|[^\x00-\xa0])+)[\x20\t\r\n\f]*(?:([*^$|!~]?=)[\x20\t\r\n\f]*(?:(['"])((?:\\.|[^\\])*?)\8|((?:\\.|[\w#-]|[^\x00-\xa0])+)|)|)[\x20\t\r\n\f]*\])*)|.*)\)|)/
说明:伪类选择器
matchExpr={
ATTR: /^\[[\x20\t\r\n\f]*((?:\\.|[\w-]|[^\x00-\xa0])+)[\x20\t\r\n\f]*(?:([*^$|!~]?=)[\x20\t\r\n\f]*(?:(['"])((?:\\.|[^\\])*?)\3|((?:\\.|[\w#-]|[^\x00-\xa0])+)|)|)[\x20\t\r\n\f]*\]/, CHILD: /^:(only|first|last|nth|nth-last)-(child|of-type)(?:\([\x20\t\r\n\f]*(even|odd|(([+-]|)(\d*)n|)[\x20\t\r\n\f]*(?:([+-]|)[\x20\t\r\n\f]*(\d+)|))[\x20\t\r\n\f]*\)|)/i, CLASS: /^\.((?:\\.|[\w-]|[^\x00-\xa0])+)/, ID: /^#((?:\\.|[\w-]|[^\x00-\xa0])+)/, PSEUDO: /^:((?:\\.|[\w-]|[^\x00-\xa0])+)(?:\(((['"])((?:\\.|[^\\])*?)\3|((?:\\.|[^\\()[\]]|\[[\x20\t\r\n\f]*((?:\\.|[\w-]|[^\x00-\xa0])+)[\x20\t\r\n\f]*(?:([*^$|!~]?=)[\x20\t\r\n\f]*(?:(['"])((?:\\.|[^\\])*?)\8|((?:\\.|[\w#-]|[^\x00-\xa0])+)|)|)[\x20\t\r\n\f]*\])*)|.*)\)|)/, TAG: /^((?:\\.|[\w*-]|[^\x00-\xa0])+)/, bool: /^(?:checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped)$/i, needsContext: /^[\x20\t\r\n\f]*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\([\x20\t\r\n\f]*((?:-\d)?\d*)[\x20\t\r\n\f]*\)|)(?=[^-]|$)/i
}
说明:matchExpr是各种正则的集合对象
rnative=/^[^{]+\{\s*\[native \w/
说明:用来判断是否是浏览器本地对象方法或属性
runescape=/\\([\da-f]{1,6}[\x20\t\r\n\f]?|([\x20\t\r\n\f])|.)/gi
说明:匹配16进制字符
funescape = function( _, escaped, escapedWhitespace ) {//_代表整个匹配项,escaped代表第一个匹配项,escapedWhitespace代表第二个分组匹配项,即空白匹配项
var high = "0x" + escaped - 0x10000;
// NaN means non-codepoint
// Support: Firefox
// Workaround erroneous numeric interpretation of +"0x"
return high !== high || escapedWhitespace ?
escaped :
// BMP codepoint
high < 0 ?
String.fromCharCode( high + 0x10000 ) :
// Supplemental Plane codepoint (surrogate pair)
String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
};
说明:解码函数,与runescape联合使用
2,sizzle中的几个函数也值得我们学习,它们给我们了一种运用思路
function assert( fn ) {
var div = document.createElement("div"); try {
return !!fn( div );
} catch (e) {
return false;
} finally {
// Remove from its parent by default
if ( div.parentNode ) {
div.parentNode.removeChild( div );
}
// release memory in IE
div = null;
}
}
说明:断言函数,用来判断某些条件是否成立,比如有没有某个属性,方法,比如:
support.attributes = assert(function( div ) {
div.className = "i";
return !div.getAttribute("className");
});
用来判断是否支持getAttribute方法。
再看elementMatcher函数:
//将之前的匹配函数综合成一个匹配函数
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];
}
matchers是一个函数数组,此方法可以把数组的多个函数合并成一个函数。
再来看看matcherFromGroupMatchers函数:
function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
var matcherCachedRuns = 0,
bySet = setMatchers.length > 0,
byElement = elementMatchers.length > 0,
//最终运行的匹配函数:
superMatcher = function( seed, context, xml, results, expandContext ) {
var elem, j, matcher,
setMatched = [],
matchedCount = 0,
i = "0",
unmatched = seed && [],
outermost = expandContext != null,
contextBackup = outermostContext,
// We must always have either seed elements or context
elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ),
// Use integer dirruns iff this is the outermost matcher
dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1); if ( outermost ) {
outermostContext = context !== document && context;
cachedruns = matcherCachedRuns;
} // Add elements passing elementMatchers directly to results
// Keep `i` a string if there are no elements so `matchedCount` will be "00" below
for ( ; (elem = elems[i]) != null; i++ ) {
if ( byElement && elem ) {
j = 0;
while ( (matcher = elementMatchers[j++]) ) {
if ( matcher( elem, context, xml ) ) {
results.push( elem );
break;
}
}
if ( outermost ) {
dirruns = dirrunsUnique;
cachedruns = ++matcherCachedRuns;
}
} // Track unmatched elements for set filters
if ( bySet ) {
// They will have gone through all possible matchers
if ( (elem = !matcher && elem) ) {
matchedCount--;
} // Lengthen the array for every element, matched or not
if ( seed ) {
unmatched.push( elem );
}
}
} // Apply set filters to unmatched elements
matchedCount += i;
if ( bySet && i !== matchedCount ) {
j = 0;
while ( (matcher = setMatchers[j++]) ) {
matcher( unmatched, setMatched, context, xml );//这里开始调用matcher 即:
} if ( seed ) {
// Reintegrate element matches to eliminate the need for sorting
if ( matchedCount > 0 ) {
while ( i-- ) {
if ( !(unmatched[i] || setMatched[i]) ) {
setMatched[i] = pop.call( results );
}
}
} // Discard index placeholder values to get only actual matches
setMatched = condense( setMatched );
} // Add matches to results
push.apply( results, setMatched ); // Seedless set matches succeeding multiple successful matchers stipulate sorting
if ( outermost && !seed && setMatched.length > 0 &&
( matchedCount + setMatchers.length ) > 1 ) { Sizzle.uniqueSort( results );
}
} // Override manipulation of globals by nested matchers
if ( outermost ) {
dirruns = dirrunsUnique;
outermostContext = contextBackup;
} return unmatched;
}; return bySet ?
markFunction( superMatcher ) :
superMatcher;
}
此函数通过闭包原理,返回终极匹配器superMatcher
在sizzle中,闭包的运用还有setMatcher
//第1个参数,preFilter,前置过滤器,相当于“div”过滤器
//第2个参数,selector,前置过滤器的字符串格式,相当于“div”input:checked + p
//第3个参数,matcher,当前位置伪类“:first”的匹配器/过滤器
//第4个参数,postFilter,后置过滤器,相当于“ ”
//第5个参数,postFinder,后置搜索器,相当于在前边过滤出来的集合里边再搜索剩下的规则的一个搜索器
//第6个参数,postSelector,后置搜索器对应的选择器字符串,相当于“input:checked + p”
//伪类选择器时会执行这个函数
function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
if ( postFilter && !postFilter[ expando ] ) {
postFilter = setMatcher( postFilter );
}
if ( postFinder && !postFinder[ expando ] ) {
postFinder = setMatcher( postFinder, postSelector );
}
return markFunction(function( seed, results, context, xml ) {
var temp, i, elem,
preMap = [],
postMap = [],
preexisting = results.length, // Get initial elements from seed or context
/*
如果执行$("ul.list>li span:eq(1)")时:则会调用multipleContexts来递归sizzle
递归后就得到elems为两个span啦
*/
elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), // Prefilter to get matcher input, preserving a map for seed-results synchronization
/*这里matcherIn就是两个span啦*/
matcherIn = preFilter && ( seed || !selector ) ?
condense( elems, preMap, preFilter, context, xml ) :
elems, matcherOut = matcher ?
// If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
postFinder || ( seed ? preFilter : preexisting || postFilter ) ? // ...intermediate processing is necessary
[] : // ...otherwise use results directly
results :
matcherIn; // Find primary matches
if ( matcher ) {
matcher( matcherIn, matcherOut, context, xml );
} // Apply postFilter
if ( postFilter ) {
temp = condense( matcherOut, postMap );
postFilter( temp, [], context, xml ); // Un-match failing elements by moving them back to matcherIn
i = temp.length;
while ( i-- ) {
if ( (elem = temp[i]) ) {
matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
}
}
} if ( seed ) {
if ( postFinder || preFilter ) {
if ( postFinder ) {
// Get the final matcherOut by condensing this intermediate into postFinder contexts
temp = [];
i = matcherOut.length;
while ( i-- ) {
if ( (elem = matcherOut[i]) ) {
// Restore matcherIn since elem is not yet a final match
temp.push( (matcherIn[i] = elem) );
}
}
postFinder( null, (matcherOut = []), temp, xml );
} // Move matched elements from seed to results to keep them synchronized
i = matcherOut.length;
while ( i-- ) {
if ( (elem = matcherOut[i]) &&
(temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) { seed[temp] = !(results[temp] = elem);
}
}
} // Add elements to results, through postFinder if defined
} else {
matcherOut = condense(
matcherOut === results ?
matcherOut.splice( preexisting, matcherOut.length ) :
matcherOut
);
if ( postFinder ) {
postFinder( null, results, matcherOut, xml );
} else {
push.apply( results, matcherOut );
}
}
});
}
这样做的好处是保证了每个匹配词的处理函数的当前执行上下文,以便后边调用。