当前位置: 首页 > news >正文

【深入浅出jQuery】源码浅析--整体架构

最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美。

其结构明晰,高内聚、低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷、渐进增强)优雅的处理能力以及 Ajax 等方面周到而强大的定制功能无不令人惊叹。

另外,阅读源码让我接触到了大量底层的知识。对原生JS 、框架设计、代码优化有了全新的认识,接下来将会写一系列关于 jQuery 解析的文章。

我在 github 上关于 jQuery 源码的全文注解,感兴趣的可以围观一下。jQuery v1.10.2 源码注解 。

系列第二篇:【深入浅出jQuery】源码浅析2--奇技淫巧

网上已经有很多解读 jQuery 源码的文章了,作为系列开篇的第一篇,思前想去起了个【深入浅出jQuery】的标题,资历尚浅,无法对 jQuery 分析的头头是道,但是 jQuery 源码当中确实有着大量巧妙的设计,不同层次水平的阅读者都能有收获,所以打算厚着脸皮将自己从中学到的一些知识点共享出来。打算从整体及分支,分章节剖析。本篇主要讲 jQuery 的整体架构及一些前期准备,先来看看 jQuery 的整体结构:

jQuery 整体架构

不同于 jQuery 代码各个模块细节实现的晦涩难懂,jQuery 整体框架的结构十分清晰,按代码行文大致分为如上图所示的模块。

初看 jQuery 源码可能很容易一头雾水,因为 9000 行的代码感觉没有尽头,所以了解作者的行文思路十分重要。

整体而言,我觉得 jQuery 采用的是总--分的结构,虽然JavaScript有着作用域的提升机制,但是 9000 多行的代码为了相互的关联性,并不代表所有的变量都要定义在最顶部。在 jQuery 中,只有全局都会用到的变量、正则表达式定义在了代码最开头,而每个模块一开始,又会定义一些只在本模块会使用到的变量、正则、方法等。所以在一开始的阅读的过程中会有很多看不懂其作用的变量,正则,方法。

所以,我觉得阅读源码很重要的一点是,摒弃面向过程的思维方式,不要刻意去追求从上至下每一句都要在一开始弄明白。很有可能一开始你在一个奇怪的方法或者变量处卡壳了,很想知道这个方法或变量的作用,然而可能它要到几千行处才被调用到。如果去追求这种逐字逐句弄清楚的方式,很有可能在碰壁几次之后阅读的积极性大受打击。

道理说了很多,接来下进入真正的正文,对 jQurey 的一些前期准备,小的细节进行分析:

jQuery 闭包结构

1

2

3

4

5

6

7

// 用一个函数域包起来,就是所谓的沙箱

// 在这里边 var 定义的变量,属于这个函数域内的局部变量,避免污染全局

// 把当前沙箱需要的外部变量通过函数参数引入进来

// 只要保证参数对内提供的接口的一致性,你还可以随意替换传进来的这个参数

(function(window, undefined) {

// jQuery 代码

})(window);

jQuery 具体的实现,都被包含在了一个立即执行函数构造的闭包里面,为了不污染全局作用域,只在后面暴露 $ 和 jQuery 这 2 个变量给外界,尽量的避开变量冲突。常用的还有另一种写法:

1

2

3

(function(window) {

// JS代码

})(window, undefined);

比较推崇的的第一种写法,也就是 jQuery 的写法。二者有何不同呢,当我们的代码运行在更早期的环境当中(pre-ES5,eg. Internet Explorer 8),undefined 仅是一个变量且它的值是可以被覆盖的。意味着你可以做这样的操作:

1

2

undefined = 42

console.log(undefined)// 42

当使用第一种方式,可以确保你需要的 undefined 确实就是 undefined。

另外不得不提出的是,jQuery 在这里有一个针对压缩优化细节,使用第一种方式,在代码压缩的时候,window 和 undefined 都可以压缩为 1 个字母并且确保它们就是 window 和 undefined。

1

2

3

4

5

// 压缩策略

// w -> windwow , u -> undefined

(function(w, u) {

})(window);

jQuery 无 new 构造

嘿,回想一下使用 jQuery 的时候,实例化一个 jQuery 对象的方法:

1

2

3

4

5

6

// 无 new 构造

$('#test').text('Test');

// 当然也可以使用 new

vartest =new$('#test');

test.text('Test');

大部分人使用 jQuery 的时候都是使用第一种无 new 的构造方式,直接 $('') 进行构造,这也是 jQuery 十分便捷的一个地方。当我们使用第一种无 new 构造方式的时候,其本质就是相当于 new jQuery(),那么在 jQuery 内部是如何实现的呢?看看:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

(function(window, undefined) {

var

// ...

jQuery =function(selector, context) {

// The jQuery object is actually just the init constructor 'enhanced'

// 看这里,实例化方法 jQuery() 实际上是调用了其拓展的原型方法 jQuery.fn.init

returnnewjQuery.fn.init(selector, context, rootjQuery);

},

// jQuery.prototype 即是 jQuery 的原型,挂载在上面的方法,即可让所有生成的 jQuery 对象使用

jQuery.fn = jQuery.prototype = {

// 实例化化方法,这个方法可以称作 jQuery 对象构造器

init:function(selector, context, rootjQuery) {

// ...

}

}

// 这一句很关键,也很绕

// jQuery 没有使用 new 运算符将 jQuery 实例化,而是直接调用其函数

// 要实现这样,那么 jQuery 就要看成一个类,且返回一个正确的实例

// 且实例还要能正确访问 jQuery 类原型上的属性与方法

// jQuery 的方式是通过原型传递解决问题,把 jQuery 的原型传递给jQuery.prototype.init.prototype

// 所以通过这个方法生成的实例 this 所指向的仍然是 jQuery.fn,所以能正确访问 jQuery 类原型上的属性与方法

jQuery.fn.init.prototype = jQuery.fn;

})(window);

大部分人初看 jQuery.fn.init.prototype = jQuery.fn 这一句都会被卡主,很是不解。但是这句真的算是 jQuery 的绝妙之处。理解这几句很重要,分点解析一下:

1)首先要明确,使用 $('xxx') 这种实例化方式,其内部调用的是 return new jQuery.fn.init(selector, context, rootjQuery) 这一句话,也就是构造实例是交给了 jQuery.fn.init() 方法去完成。

2)将 jQuery.fn.init 的 prototype 属性设置为 jQuery.fn,那么使用 new jQuery.fn.init() 生成的对象的原型对象就是 jQuery.fn ,所以挂载到 jQuery.fn 上面的函数就相当于挂载到 jQuery.fn.init() 生成的 jQuery 对象上,所有使用 new jQuery.fn.init() 生成的对象也能够访问到 jQuery.fn 上的所有原型方法。

3)也就是实例化方法存在这么一个关系链

  • jQuery.fn.init.prototype = jQuery.fn = jQuery.prototype ;
  • new jQuery.fn.init() 相当于 new jQuery() ;
  • jQuery() 返回的是 new jQuery.fn.init(),而 var obj = new jQuery(),所以这 2 者是相当的,所以我们可以无 new 实例化 jQuery 对象。

jQuery 方法的重载

jQuery 源码晦涩难读的另一个原因是,使用了大量的方法重载,但是用起来却很方便:

1

2

3

4

5

6

7

8

9

// 获取 title 属性的值

$('#id').attr('title');

// 设置 title 属性的值

$('#id').attr('title','jQuery');

// 获取 css 某个属性的值

$('#id').css('title');

// 设置 css 某个属性的值

$('#id').css('width','200px');

方法的重载即是一个方法实现多种功能,经常又是 get 又是 set,虽然阅读起来十分不易,但是从实用性的角度考虑,这也是为什么 jQuery 如此受欢迎的原因,大多数人使用 jQuery() 构造方法使用的最多的就是直接实例化一个 jQuery 对象,但其实在它的内部实现中,有着 9 种不同的方法重载场景:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

// 接受一个字符串,其中包含了用于匹配元素集合的 CSS 选择器

jQuery([selector,[context]])

// 传入单个 DOM

jQuery(element)

// 传入 DOM 数组

jQuery(elementArray)

// 传入 JS 对象

jQuery(object)

// 传入 jQuery 对象

jQuery(jQuery object)

// 传入原始 HTML 的字符串来创建 DOM 元素

jQuery(html,[ownerDocument])

jQuery(html,[attributes])

// 传入空参数

jQuery()

// 绑定一个在 DOM 文档载入完成后执行的函数

jQuery(callback)

所以读源码的时候,很重要的一点是结合 jQuery API 进行阅读,去了解方法重载了多少种功能,同时我想说的是,jQuery 源码有些方法的实现特别长且繁琐,因为 jQuery 本身作为一个通用性特别强的框架,一个方法兼容了许多情况,也允许用户传入各种不同的参数,导致内部处理的逻辑十分复杂,所以当解读一个方法的时候感觉到了明显的困难,尝试着跳出卡壳的那段代码本身,站在更高的维度去思考这些复杂的逻辑是为了处理或兼容什么,是否是重载,为什么要这样写,一定会有不一样的收获。其次,也是因为这个原因,jQuery 源码存在许多兼容低版本的 HACK 或者逻辑十分晦涩繁琐的代码片段,浏览器兼容这样的大坑极其容易让一个前端工程师不能学到编程的精髓,所以不要太执着于一些边角料,即使兼容性很重要,也应该适度学习理解,适可而止。

http://www.jsqmd.com/news/1112390/

相关文章:

  • 后端可观测性排障:先问用户受影响了吗
  • 【计算机Java毕业设计案例】基于 SpringBoot 的线上教学资源评价与收藏管理系统的设计与实现 中小学数字化教育资源库管理平台(程序+文档+讲解+定制)
  • 以主站为参考时钟实现主从DC同步方案及原理深度剖析(2):计算从站初始偏移量
  • 【OpenHarmony/HarmonyOs 】ArkUI 实现闪卡翻转记忆与掌握度统计:概念复习页面完整拆解
  • 量子机器学习中的噪声挑战与纠错技术
  • 3分钟掌握Maye:终极Windows快速启动工具完全指南
  • 我眼中的领域驱动设计
  • 00668,湘江新区的“尖子生”交卷了!
  • Verilog FFT 设计
  • Adobe-GenP 3.0:基于AutoIt的Adobe CC授权验证绕过技术实现
  • 计算机毕业设计之jsp-驾校预约管理系统
  • 鸿升光HSGQ PON全光网络-三网融合解决方案
  • Codex封装Skill三步法:从一次性对话到可复用自动化工作流
  • 企业仓储数字化如何落地?不同规模仓库WMS仓储系统举例
  • 选对取代度提升包封率!近红外羧基染料 DiR-COOH 全解析
  • AI系统部署后组织效能下降问题剖析:单一工具引入无法驱动业务增长的底层架构原因
  • 电容式触控感应原理,Q-Touch:针对不同的覆盖层厚度或 PCB 布局微调灵敏度 ,快速构建项目
  • 革命性魔兽世界宏引擎:GSE如何重新定义技能自动化
  • 5步掌握Path of Building PoE2:免费开源的角色构建终极解决方案
  • 【系统维护】C盘爆满解决方案:Wise Disk Cleaner 绿色版实操指南
  • 工业级航班延误预测系统:XGBoost端到端落地实践
  • Java计算机毕设之基于 SpringBoot 的中小学优质教学资源推送服务系统的设计与实现 智慧教育背景下中小学教学资源运维系统(完整前后端代码+说明文档+LW,调试定制等)
  • 【信道估计】基于太赫兹集成UM-MIMO和IRS系统的混合球面与平面波信道建模与估计Matlab仿真
  • Win7系统上安装Python教程:轻松上手3.8.6版本
  • 密码学博客:AES-CBC 比特翻转(Bit Flipping)攻击原理、实战与防御
  • 新能源构网型光伏PV+储能SOC+虚拟同步机(VSG)并网逆变器仿真(仿真+参考文献)
  • Android SSL证书固定实战:原理、方案与避坑指南
  • 【刷题日记】LeetCode 21. 合并两个有序列表
  • 医疗电子PCB设计指南:中频理疗仪电路板关键技术
  • 首个threejs项目-前端填坑指南