jQuery类库的设计
目前为止,jquery是js社区中最活跃、用户最多的前端类库,具有链式操作、兼容性、基于数组的操作、强大的插件机制等特点,也是很多前端入门同学最早接触到的库。但是内部如何实现的,一直吸引着我。因此最近三个月读完了jquery1.7版本的设计,之所以选择该版本是因为Sizzle在1.8之后引入了编译函数,代码变动比较大。
1.总体设计
本文对jquery1.7版本进行了阅读学习,将整个jquery源码拆分为11个模块,这些模块相互依赖,构成了一个简单、强大的js类库。jquery是一个基于DOM操作的类库,因此Sizzle选择器引擎的实现就显得尤为重要。针对Sizzle选择器引擎的实现,之前已经做过先关的分析,参见:sizzle选择器引擎介绍 。下面对其中的数据存储、事件处理、异常请求ajax、动画等进行简单的介绍。
(function(window, undefined) {
var jQuery = function(selector, context) {
return new jQuery.fn.init(selector, context, rootJquery);
}
jQuery.fn = function() {
// 原型对象
...
}
// 工具方法
// 异步队列
// 队列queue
// 浏览器测试support
// 属性操作
// 事件系统
// DOM遍历与操作、样式操作
// ajax请求
// 动画
})(window)
2. 数据存储
在实际的项目开发中,经常需要把某些信息附加到一个DOM节点中,那么如果管理DOM节点和附加数据的关系,就显得非常重要。很明显,目前有两种思路来解决这个问题:1)直接附加在DOM节点上;2)通过一个id来关联DOM节点、附加数据。这两种方法各有利弊:方法1的好处是DOM节点、附加数据在一起,便于维护;方法2的好处是可以避免相互依赖,从而避免内存泄露问题。
var arr = [];
function createNode() {
return document.createElement('div');
}
function saveNodes() {
for(var i=0; i<100; i++) {
arr.push(createNode());
}
}
// 将上述100个nodes节点渲染到页面,之后在将其从页面中剔除掉,那么因为arr引用着100个nodes节点,这些节点就无法被垃圾回收机制回收,从而引起内存泄露问题。
3. 异步Deferred
jQuery中关于异步的实现,大致上遵循Promise/A规范,为什么说是大致呢?
- then的返回:then方法并没有返回新的异步对象,这不符合Promise/A规范中的then要求。这一点在jQuery2.0以后已经被修改
- 结果处理:在进行结果处理的时候,jQuery并没有进行结果的异常捕获
- 参数的个数:jquery中resolve可以有多个参数,而Promise/A的resolve仅能有一个
内部实现比较曲折抽象,代码晦涩难懂,主要是通过"once"、"memory"两个参数进行控制:"once"决定异步的回调函数只能被执行一次;“memory”决定函数具有记忆动能,也就是异步事件完成以后,再绑定回调函数,回调函数会立即执行。
4. 事件处理
jquery内部事件的功能很强大,除了可以处理DOM事件外,还可以自定义事件、触发事件(DOM事件、自定义事件)、定义事件的命名空间等强大功能。并且jquery内部的另外一大亮点就是通过数据存储模块(.data),尽量降低DOM和监听事件之间的依赖,避免DOM、js对象相互依赖造成的内存泄露。在数据缓存模块的数据结构如下:
$('.a').on('click', function() {});
// results is as fellow:
$.cache = {
1: {
events: {
click: [ //click.delegateCount: 记录代理事件的个数,代理回调函数放在数组的前面
{
data: ...,
guid: ...,
selector: ...,
handler: function() {}, // handler.guid = 1 用于定位和移除监听函数
....
}
]
},
handle: function() {......} // 主监听函数
}
}
4.1 事件绑定:
当绑定事件时,内部方法的调用链为:bind/delegate/live/one()--->.on()—>$.event.add()—>$.data()/addEventListener/attachEvent()。其实在对于一个DOM元素,所有的事件都对应一个主监听函数($._data(elem).handle),然后通过主监听函数通过事件分发函数($.event.dispatch)来触发相应类型的监听函数。
$('.a').on('click', fn1);
$('.a').on('blur', fn2);
$('.a').on('focus', fn3);
// 其实DOM元素(.a)并没有直接与fn1、fn2、fn3关联起来,而是通过dispatch进行事件分发
// 用伪代码可以表示如下:
$('.a').on('click blur focus', dispatch);
function dispatch(type) {
var fn;
if(type === 'click') {
fn = fn1;
} else if(type === 'blur') {
fn = fn2;
} else if (type === 'focus') {
fn = fn3;
}
return fn;
}
4. 2 事件移除
当移除事件时,内部方法的调用链为:unbind/undelagate/die()--->.off()—>$.event.remove()—>$._data()/removeEventListener/detachEvent()。事件的移除,也就是从数据存储对象$.cache中移除相应的事件对象,当事件对象events为空时,则移除整个数据缓存对象。
4.3 事件手动触发
在jquery中,可以手工触发DOM事件或自定义事件。内部方法的调用链为:.trigger/triggerHandler() —> $.event.trigger() — > $.event.dispatch(主监听函数) — >事件的监听函数。
还有一个需要关注的问题就是,如何事件事件的冒泡呢?方法其实很简单,就是根据DOM的结构向上查询出元素的祖先元素,一直到window对象,这样就构成了元素的冒泡路径,然后触发这个路径上元素的相应事件。这也是jquery可以模拟focus、blur、change、submit进行事件冒泡的关键环节。
// 寻找冒泡路径
var eventPath = [];
for(;cur; cur = cur.parentNode) {
eventPath.push([cur, type]);
}
// 执行路径上的监听函数
for() {
cur = eventPath[i][0];
type = eventPath[i][1];
hanle = $._data(cur, "events")[type];
hanle.apply(cur);
}
5. 异步请求ajax
异步请求是jquery在总体可以分为三部分:核心实现、便捷方法、ajax全局事件。其中该模块依赖于Deferred模块提供的异步编程模块,可以方便进行回调函数的注册,例如:
$(url, options).then(successFn, failFn);
其中核心方法的实现主要包括以下骤:
- 参数的设置:在jQuery所有API中,.ajax的参数种类应该是对多的,里面的参数看得人掩护缭乱。但是其中最终的有url、type、dataType、data,尤其是dataType的设置对于结果的应该很长大,所以有大量代码是对这一步的处理。
- 前置过滤函数处理:主要是对json、jsonp、script三种数据类型的处理,在请求发送前对其进行过滤处理
- 请求发出:这里请求的发出包括两种方式,分别为依赖于XMLHttpRequest和script标签。如果浏览器允许跨域,则仅需使用XMLHttpRequest就足够了。
- 回调函数的执行:这里的回调函数包括很多种,请求发出前、开始接受数据、数据接受完成等等。
6. 动画解析
在jQuery中,动画show、hide、fadeIn、fadeOut等均要调用Animation方法,也就是说Animation是最基本的入口函数。在上图中可以看到该入口函数包括了三个过程:参数配置、生成动画函数、动画函数执行。下面展开包括以下细节:
- 参数配置:主要有三个参数,duration表示动画的执行时间;easing为动画每一帧的变化速度,目前jQuery中仅存在两种帧变化函数:线性(linear)变化、余弦变化(swing),要用其他变化函数只能修改$.fx.easing对象;complete为动画完成之后需要执行的回调函数。
- 生成动画函数doAnimation:jQuery会给每一个样式生成一个$.fx对象,该对象用于实现动画效果。默认情况下,每隔13ms会执行一帧动画,然后更新页面的样式。
- 动画执行:如果动画不需要排队,在生成完动画函数之后,就立即执行动画函数doAnimation;反之,将doAnimation放入队列queue中进行排队。当所有动画均完成之后,就可以执行回调函数complete了。
当然,jQuery内部实现比较复杂,考虑了函数暂停、样式临时修改(修改inline元素的width/height时,会临时将其display修改为inline-block)、清空动画队列等操作。
jQuery类库的设计的更多相关文章
- jQuery 源码解析一:jQuery 类库整体架构设计解析
如果是做 web 的话,相信都要对 Dom 进行增删查改,那大家都或多或少接触到过 jQuery 类库,其最大特色就是强大的选择器,让开发者脱离原生 JS 一大堆 getElementById.get ...
- 第十七章:jQuery类库
javascript的核心API设计的很简单,但由于浏览器之间的不兼容性,导致客户端的API过于复杂.IE9的到来缓解了这种情况.然而使用javascript的框架或者工具类库,能简化通用操作,处理浏 ...
- BootStrap入门教程 (四) :JQuery类库插件(模态窗口,滚动监控,标签效果,提示效果,“泡芙”效果,警告区域,折叠效果,旋转木马,输入提示)
上讲回顾:Bootstrap组件丰富同时具有良好可扩展性,能够很好地应用在生产环境.这些组件包括按钮(Button),导航(Navigation),缩略图( thumbnails),提醒(Alert) ...
- 基于jquery类库的绘制二维码的插件jquery.qrcode.js
jquery.qrcode.min.js 如下 (function(r){r.fn.qrcode=function(h){var s;function u(a){this.mode=s;this.d ...
- Atitit.json类库的设计与实现 ati json lib
Atitit.json类库的设计与实现 ati json lib 1. 目前jsonlib库可能有问题,可能版本冲突,抛出ex1 2. 解决之道:1 2.1. 自定义json解析库,使用多个复合的js ...
- 判断jquery类库是否加载,如未加载则加载。
本人所有文章使用到的东西均在"渭南电脑维修网"网站中得以实现和应用,还请大家参考. 抄写别人网站的同时,N多不同的网站,势必有N多的css.javascript引用文件都会重复引用 ...
- JavaScript : 零基础打造自己的jquery类库
写作不易,转载请注明出处,谢谢. 文章类别:Javascript基础(面向初学者) 前言 在之前的章节中,我们已经不依赖jQuery,单纯地用JavaScript封装了很多方法,这个时候,你一定会想, ...
- 浅谈 jQuery 核心架构设计
jQuery对于大家而言并不陌生,因此关于它是什么以及它的作用,在这里我就不多言了,而本篇文章的目的是想通过对源码简单的分析来讨论 jQuery 的核心架构设计,以及jQuery 是如何利用javas ...
- 谈一谈jQuery核心架构设计(转)
jQuery对于大家而言并不陌生,因此关于它是什么以及它的作用,在这里我就不多言了,而本篇文章的目的是想通过对源码简单的分析来讨论 jQuery 的核心架构设计,以及jQuery 是如何利用javas ...
随机推荐
- iOS中几种定时器
在软件开发过程中,我们常常需要在某个时间后执行某个方法,或者是按照某个周期一直执行某个方法.在这个时候,我们就需要用到定时器. iOS中定时器NSTimer的使用 1.初始化 + (NSTimer ...
- JavaScript和html5 canvas生成圆形印章
代码: function createSeal(id,company,name){ var canvas = document.getElementById(id); var context = ca ...
- [USACO2005][POJ2228]Naptime(对特殊环状DP的处理)
题目:http://poj.org/problem?id=2228 题意:将一天分为N小时,每小时都有一个价值w,有一头牛要睡觉,而它的睡觉是连续的,且第一小时不能算价值,即如果你睡了[a,b],则你 ...
- F12调试打开时,出现很多多余内容问题解决
关闭vs2013的browserlink功能即可: <appSettings> <add key="vs:EnableBrowserLink" value=&qu ...
- 微信支付PHP SDK —— 公众号支付代码详解
在微信支付 开发者文档页面 下载最新的 php SDK http://mch.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1 这里假设你已经申请完微 ...
- ansible-3 主机清单hosts的设置
主机清单的设置参考:http://www.ansible.com.cn/docs/intro_inventory.html [ceshi]192.168.220.98log ansible_ssh_h ...
- Svn-在eclipse中安装svn插件
在eclipse中安装svn有两种方式 1:直接下载svn的插件包安装 使用的版本为1.8.x Links for 1.8.x Release: Eclipse update site URL: ht ...
- CAS做单点登陆(SSO)——集成BIEE 11g
BIEE 11G和CAS集成零代码编写,只需配置. 更改BIEE analytics应用的web.xml 将analytics.war解包(使用7-zip或者Win-rar就可以),然后修改WEB-I ...
- ElasticSearch插件安装Head、Kopf与Bigdesk
ElasticSearch-Head ElasticSearch-Head 是一个与Elastic集群(Cluster)相交互的Web前台. ES-Head的主要作用 它展现ES集群的拓扑结构,并且可 ...
- 【poj2724】 Purifying Machine
http://poj.org/problem?id=2724 (题目链接) 题意 Mike有一个机器可以帮助他清理奶酪,每个奶酪由一个n位二进制数表示,机器上一共有n个按钮,每个按钮有1,0,*,其中 ...