文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

jQuery 2.0.3如何用源码分析Sizzle引擎

2024-04-02 19:55

关注

本篇文章给大家分享的是有关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为位置词素

根据关系选择器检查

如果是这类没有位置词素的选择器:&rsquo;#id.aaron[name="checkbox"]&lsquo;

从右到左依次看看当前节点elem是否匹配规则即可。但是由于有了位置词素,

那么判断的时候就不是简单判断当前节点了,

可能需要判断elem的兄弟或者父亲节点是否依次符合规则。

这是一个递归深搜的过程。

所以matchers又经过一层包装了

然后用同样的方式递归下去,直接到tokens分解完毕

返回的结果一个根据关系选择器分组后在组合的嵌套很深的闭包函数了

看看结构

jQuery 2.0.3如何用源码分析Sizzle引擎

但是组合之后怎么执行?

superMatcher方法是matcherFromGroupMatchers( elementMatchers, setMatchers )方法return出来的,但是执行起重要作用的是它

以上就是jQuery 2.0.3如何用源码分析Sizzle引擎,小编相信有部分知识点可能是我们日常工作会见到或用到的。希望你能通过这篇文章学到更多知识。更多详情敬请关注编程网行业资讯频道。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-前端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯