sizzle源码分析 (4)sizzle 技术总结及值得我们学习的地方
分析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 ); } } }); }
这样做的好处是保证了每个匹配词的处理函数的当前执行上下文,以便后边调用。