本篇文章给大家分享的是有关jQuery 2.0.3如何用源码分析Sizzle引擎,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。
什么是JavaScript的“预编译”?
function Aaron() { alert("hello"); }; Aaron(); //这里调用Aaron,输出world而不是hello function Aaron() { alert("world"); }; Aaron(); //这里调用Aaron,当然输出world
按理说,两个签名完全相同的函数,在其他编程语言中应该是非法的。但在JavaScript中,这没错。不过,程序运行之后却发现一个奇怪的现象:两次调用都只是***那个函数里输出的值!显然***个函数没有起到任何作用。这又是为什么呢?
JavaScript执行引擎并非一行一行地分析和执行程序,而是一段一段地进行预编译后让后 再执行的。而且,在同一段程序中,函数 在被执行之前 会被预定义,后定定义的 同名函数 会覆盖 先定义的函数。在调用函数的时候,只会调用后一个预定义的函数(因为后一个预定义的函数把前一个预定义的函数覆盖了)。也就是说,在***次调用myfunc之前,***个函数语句定义的代码逻辑,已被第二个函数定义语句覆盖了。所以,两次都调用都是执行***一个函数逻辑了。
我们用实际证明下:
/ ) { var i, setMatchers = [], elementMatchers = [], cached = compilerCache[selector + " "]; if (!cached) { //依旧看看有没有缓存 // Generate a function of recursive functions that can be used to check each element if (!group) { //如果没有词法解析过 group = tokenize(selector); } i = group.length; //从后开始生成匹配器 //如果是有并联选择器这里多次等循环 while (i--) { //这里用matcherFromTokens来生成对应Token的匹配器 cached = matcherFromTokens(group[i]); if (cached[expando]) { setMatchers.push(cached); } else { //普通的那些匹配器都压入了elementMatchers里边 elementMatchers.push(cached); } } // Cache the compiled function // 这里可以看到,是通过matcherFromGroupMatchers这个函数来生成最终的匹配器 cached = compilerCache(selector, matcherFromGroupMatchers(elementMatchers, setMatchers)); } //把这个***匹配器返回到select函数中 return cached; };
matcherFromTokens
1: //生成用于匹配单个选择器组的函数 2: //充当了selector“tokens”与Expr中定义的匹配方法的串联与纽带的作用, 3: //可以说选择符的各种排列组合都是能适应的了 4: //Sizzle巧妙的就是它没有直接将拿到的“分词”结果与Expr中的方法逐个匹配逐个执行, 5: //而是先根据规则组合出一个大的匹配方法,***一步执行。但是组合之后怎么执行的 6: function matcherFromTokens(tokens) { 7: var checkContext, matcher, j, 8: len = tokens.length, 9: leadingRelative = Expr.relative[tokens[0].type], 10: implicitRelative = leadingRelative || Expr.relative[" "], //亲密度关系 11: i = leadingRelative ? 1 : 0, 12: 13: // The foundational matcher ensures that elements are reachable from top-level context(s) 14: // 确保这些元素可以在context中找到 15: matchContext = addCombinator(function(elem) { 16: return elem === checkContext; 17: }, implicitRelative, true), 18: matchAnyContext = addCombinator(function(elem) { 19: return indexOf.call(checkContext, elem) > -1; 20: }, implicitRelative, true), 21: 22: //这里用来确定元素在哪个context 23: matchers = [ 24: function(elem, context, xml) { 25: return (!leadingRelative && (xml || context !== outermostContext)) || ( 26: (checkContext = context).nodeType ? 27: matchContext(elem, context, xml) : 28: matchAnyContext(elem, context, xml)); 29: } 30: ]; 31: 32: for (; i < len; i++) { 33: // Expr.relative 匹配关系选择器类型 34: // "空 > ~ +" 35: if ((matcher = Expr.relative[tokens[i].type])) { 36: //当遇到关系选择器时elementMatcher函数将matchers数组中的函数生成一个函数 37: //(elementMatcher利用了闭包所以matchers一直存在内存中) 38: matchers = [addCombinator(elementMatcher(matchers), matcher)]; 39: } else { 40: //过滤 ATTR CHILD CLASS ID PSEUDO TAG 41: matcher = Expr.filter[tokens[i].type].apply(null, tokens[i].matches); 42: 43: // Return special upon seeing a positional matcher 44: //返回一个特殊的位置匹配函数 45: //伪类会把selector分两部分 46: if (matcher[expando]) { 47: // Find the next relative operator (if any) for proper handling 48: // 发现下一个关系操作符(如果有话)并做适当处理 49: j = ++i; 50: for (; j < len; j++) { 51: if (Expr.relative[tokens[j].type]) { //如果位置伪类后面还有关系选择器还需要筛选 52: break; 53: } 54: } 55: return setMatcher( 56: i > 1 && elementMatcher(matchers), 57: i > 1 && toSelector( 58: // If the preceding token was a descendant combinator, insert an implicit any-element `*` 59: tokens.slice(0, i - 1).concat({ 60: value: tokens[i - 2].type === " " ? "*" : "" 61: }) 62: ).replace(rtrim, "$1"), 63: matcher, 64: i < j && matcherFromTokens(tokens.slice(i, j)), //如果位置伪类后面还有选择器需要筛选 65: j < len && matcherFromTokens((tokenstokens = tokens.slice(j))), //如果位置伪类后面还有关系选择器还需要筛选 66: j < len && toSelector(tokens) 67: ); 68: } 69: matchers.push(matcher); 70: } 71: } 72: 73: return elementMatcher(matchers); 74: }
重点就是
cached = matcherFromTokens(group[i]);
cached 的结果就是matcherFromTokens返回的matchers编译函数了
matcherFromTokens的分解是有规律的:
语义节点+关系选择器的组合
div > p + div.aaron input[type="checkbox"]
Expr.relative 匹配关系选择器类型
当遇到关系选择器时elementMatcher函数将matchers数组中的函数生成一个函数
在递归分解tokens中的词法元素时
提出***个typ匹配到对应的处理方法
matcher = Expr.filter[tokens[i].type].apply(null, tokens[i].matches); "TAG": function(nodeNameSelector) { var nodeName = nodeNameSelector.replace(runescape, funescape).toLowerCase(); return nodeNameSelector === "*" ? function() { return true; } : function(elem) { return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; }; },
matcher其实最终结果返回的就是bool值,但是这里返回只是一个闭包函数,不会马上执行,这个过程换句话就是 编译成一个匿名函数
继续往下分解
如果遇到关系选着符就会合并分组了
matchers = [addCombinator(elementMatcher(matchers), matcher)];
通过elementMatcher生成一个***匹配器
function elementMatcher(matchers) { //生成一个***匹配器 return matchers.length > 1 ? //如果是多个匹配器的情况,那么就需要elem符合全部匹配器规则 function(elem, context, xml) { var i = matchers.length; //从右到左开始匹配 while (i--) { //如果有一个没匹配中,那就说明该节点elem不符合规则 if (!matchers[i](elem, context, xml)) { return false; } } return true; } : //单个匹配器的话就返回自己即可 matchers[0]; }
看代码大概就知道,就是分解这个子匹配器了,返回又一个curry函数,给addCombinator方法
//addCombinator方法就是为了生成有位置词素的匹配器。 function addCombinator(matcher, combinator, base) { var dir = combinator.dir, checkNonElements = base && dir === "parentNode", donedoneName = done++; //第几个关系选择器 return combinator.first ? // Check against closest ancestor/preceding element // 检查最靠近的祖先元素 // 如果是紧密关系的位置词素 function(elem, context, xml) { while ((elemelem = elem[dir])) { if (elem.nodeType === 1 || checkNonElements) { //找到***个亲密的节点,立马就用***匹配器判断这个节点是否符合前面的规则 return matcher(elem, context, xml); } } } : // Check against all ancestor/preceding elements //检查最靠近的祖先元素或兄弟元素(概据>、~、+还有空格检查) //如果是不紧密关系的位置词素 function(elem, context, xml) { var data, cache, outerCache, dirkey = dirruns + " " + doneName; // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching // 我们不可以在xml节点上设置任意数据,所以它们不会从dir缓存中受益 if (xml) { while ((elemelem = elem[dir])) { if (elem.nodeType === 1 || checkNonElements) { if (matcher(elem, context, xml)) { return true; } } } } else { while ((elemelem = elem[dir])) { //如果是不紧密的位置关系 //那么一直匹配到true为止 //例如祖宗关系的话,就一直找父亲节点直到有一个祖先节点符合规则为止 if (elem.nodeType === 1 || checkNonElements) { outerCache = elem[expando] || (elem[expando] = {}); //如果有缓存且符合下列条件则不用再次调用matcher函数 if ((cache = outerCache[dir]) && cache[0] === dirkey) { if ((data = cache[1]) === true || data === cachedruns) { return data === true; } } else { cache = outerCache[dir] = [dirkey]; cache[1] = matcher(elem, context, xml) || cachedruns; //cachedruns//正在匹配第几个元素 if (cache[1] === true) { return true; } } } } } }; }
matcher为当前词素前的“匹配器”
combinator为位置词素
根据关系选择器检查
如果是这类没有位置词素的选择器:’#id.aaron[name="checkbox"]‘
从右到左依次看看当前节点elem是否匹配规则即可。但是由于有了位置词素,
那么判断的时候就不是简单判断当前节点了,
可能需要判断elem的兄弟或者父亲节点是否依次符合规则。
这是一个递归深搜的过程。
所以matchers又经过一层包装了
然后用同样的方式递归下去,直接到tokens分解完毕
返回的结果一个根据关系选择器分组后在组合的嵌套很深的闭包函数了
看看结构
但是组合之后怎么执行?
superMatcher方法是matcherFromGroupMatchers( elementMatchers, setMatchers )方法return出来的,但是执行起重要作用的是它
以上就是jQuery 2.0.3如何用源码分析Sizzle引擎,小编相信有部分知识点可能是我们日常工作会见到或用到的。希望你能通过这篇文章学到更多知识。更多详情敬请关注编程网行业资讯频道。