iis服务器助手广告广告
返回顶部
首页 > 资讯 > 前端开发 > VUE >jQuery 2.0.3如何用源码分析Sizzle引擎
  • 738
分享到

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

2024-04-02 19:04:59 738人浏览 安东尼
摘要

本篇文章给大家分享的是有关Jquery 2.0.3如何用源码分析Sizzle引擎,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。什么是JavaS

本篇文章给大家分享的是有关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引擎,小编相信有部分知识点可能是我们日常工作会见到或用到的。希望你能通过这篇文章学到更多知识。更多详情敬请关注编程网VUE频道。

--结束END--

本文标题: jQuery 2.0.3如何用源码分析Sizzle引擎

本文链接: https://www.lsjlt.com/news/85702.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • jQuery 2.0.3如何用源码分析Sizzle引擎
    本篇文章给大家分享的是有关jQuery 2.0.3如何用源码分析Sizzle引擎,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。什么是JavaS...
    99+
    2024-04-02
  • 如何用jQuery 2.0.3源码分析Deferred
    本篇文章给大家分享的是有关如何用jQuery 2.0.3源码分析Deferred,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。构建Deferr...
    99+
    2024-04-02
  • jquery源码如何解析符号
    jQuery源码是一个相当庞大和复杂的项目,包含数千行JavaScript代码和许多符号,这使得解析它变得非常困难。 在本文中,我们将探讨一些方法和技巧,可以用来更好地解析jQuery源码中的符号。首先,我们需要了解一些基本的JavaScr...
    99+
    2023-05-18
  • 如何进行jQuery EasyUI 1.2.6源码合集的分析
    如何进行jQuery EasyUI 1.2.6源码合集的分析,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。相信关注过jQuer...
    99+
    2024-04-02
  • 如何进行jQuery源码的整体框架分析
    这篇文章将为大家详细讲解有关如何进行jQuery源码的整体框架分析,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。先附上jQuery的代码结构。JS代码(fu...
    99+
    2024-04-02
  • 如何用源代码分析FileZilla
    这期内容当中小编将会给大家带来有关如何用源代码分析FileZilla,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。FileZilla是一种快速、可信赖的FTP客户端以及服务器端开放源代码程式,具有多种特色...
    99+
    2023-06-16
  • 如何用源码分析Java HashMap实例
    本篇文章为大家展示了如何用源码分析Java HashMap实例,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。引言HashMap在键值对存储中被经常使用,那么它到底是如何实现键值存储的呢?一 Entr...
    99+
    2023-06-17
  • Java 如何使用Velocity引擎生成代码
    目录原理实战原理 其原理如图: 1.配置数据源信息(包括表名) 2.读取数据表字段信息:列名、类型、字段注释、表注释 3.编写代码模板,并将该模板加载到内存 4.根据模板所需,组装...
    99+
    2024-04-02
  • python如何使用contextvars模块源码分析
    目录前记更新说明1.有无上下文传变量的区别2.如何使用contextvars模块3.如何优雅的使用contextvars4.contextvars的原理4.1 ContextMeta...
    99+
    2024-04-02
  • 如何分析Linux内核源码do_fork
    本篇文章为大家展示了如何分析Linux内核源码do_fork,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。我们都知道进程是Linux内核中最为重要的一个抽象概念,那么我们平时在fork一个进程时,该...
    99+
    2023-06-16
  • 如何用源码分析在linux上的safe point
    这篇文章将为大家详细讲解有关如何用源码分析在linux上的safe point,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。safe point 顾明思意,就是安全点,当需要jvm做一些操作...
    99+
    2023-06-17
  • 如何进行FileZilla的源代码分析
    这篇文章将为大家详细讲解有关如何进行FileZilla的源代码分析,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。FileZilla是一种快速、可信赖的FTP客户端以及服务器端开放源代码程式,...
    99+
    2023-06-16
  • 如何用Play源代码分析Server启动过程
    这期内容当中小编将会给大家带来有关如何用Play源代码分析Server启动过程,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。Play是个Rails风格的Java Web框架。如何调试请看此处。以下进入正题...
    99+
    2023-06-17
  • Nodejs v14源码分析之如何使用Event模块
    本篇内容主要讲解“Nodejs v14源码分析之如何使用Event模块”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Nodejs v14源码分析之如何使用Eve...
    99+
    2024-04-02
  • 如何分析MySQL常见的两种存储引擎MyISAM与InnoDB
    本篇文章为大家展示了如何分析MySQL常见的两种存储引擎MyISAM与InnoDB,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。一 MyISAM1.1 MyISAM简介MyISAM是MySQL的默认...
    99+
    2023-06-19
  • 源码分析C++是如何实现string的
    目录深拷贝下string的实现COW方式下string的实现data()和c_str()的区别to_string是怎么实现的读完本文相信您可以回答以下问题: string的常见的实现...
    99+
    2023-05-14
    C++实现string源码 C++实现string C++ string
  • MySQL 驱动中虚引用 GC 耗时优化与源码分析
    目录本文要点:背景优化——由暴力破解到优雅配置什么是虚引用关联对象真的被回收了吗虚引用的使用场景——mysql-connector-Java 虚引用源码分析总结本文要点: ...
    99+
    2023-05-16
    MySQL 驱动虚引用GC 耗时 MySQL 驱动虚引用
  • 如何用源码分析Struts2请求处理及过程
    这期内容当中小编将会给大家带来有关如何用源码分析Struts2请求处理及过程,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。1.1 Struts2请求处理1. 一个请求在Struts2框架中的处理步骤:a)...
    99+
    2023-06-17
  • 如何进行MySQL INNODB存储引擎中各类型SQL加锁分析
    这篇文章给大家介绍如何进行MySQL INNODB存储引擎中各类型SQL加锁分析,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。 MYSQL5.6事物隔离级...
    99+
    2024-04-02
  • 如何分析Go语言的库源码文件
    这期内容当中小编将会给大家带来有关如何分析Go语言的库源码文件,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。go适合做什么go是golang...
    99+
    2024-04-02
软考高级职称资格查询
推荐阅读
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作