目前为止,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规范,为什么说是大致呢?

  1. then的返回:then方法并没有返回新的异步对象,这不符合Promise/A规范中的then要求。这一点在jQuery2.0以后已经被修改
  2. 结果处理:在进行结果处理的时候,jQuery并没有进行结果的异常捕获
  3. 参数的个数: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);

其中核心方法的实现主要包括以下骤:

  1. 参数的设置:在jQuery所有API中,.ajax的参数种类应该是对多的,里面的参数看得人掩护缭乱。但是其中最终的有url、type、dataType、data,尤其是dataType的设置对于结果的应该很长大,所以有大量代码是对这一步的处理。
  2. 前置过滤函数处理:主要是对json、jsonp、script三种数据类型的处理,在请求发送前对其进行过滤处理
  3. 请求发出:这里请求的发出包括两种方式,分别为依赖于XMLHttpRequest和script标签。如果浏览器允许跨域,则仅需使用XMLHttpRequest就足够了。
  4. 回调函数的执行:这里的回调函数包括很多种,请求发出前、开始接受数据、数据接受完成等等。

6. 动画解析

在jQuery中,动画show、hide、fadeIn、fadeOut等均要调用Animation方法,也就是说Animation是最基本的入口函数。在上图中可以看到该入口函数包括了三个过程:参数配置、生成动画函数、动画函数执行。下面展开包括以下细节:

  1. 参数配置:主要有三个参数,duration表示动画的执行时间;easing为动画每一帧的变化速度,目前jQuery中仅存在两种帧变化函数:线性(linear)变化、余弦变化(swing),要用其他变化函数只能修改$.fx.easing对象;complete为动画完成之后需要执行的回调函数。
  2. 生成动画函数doAnimation:jQuery会给每一个样式生成一个$.fx对象,该对象用于实现动画效果。默认情况下,每隔13ms会执行一帧动画,然后更新页面的样式。
  3. 动画执行:如果动画不需要排队,在生成完动画函数之后,就立即执行动画函数doAnimation;反之,将doAnimation放入队列queue中进行排队。当所有动画均完成之后,就可以执行回调函数complete了。

当然,jQuery内部实现比较复杂,考虑了函数暂停、样式临时修改(修改inline元素的width/height时,会临时将其display修改为inline-block)、清空动画队列等操作。

jQuery类库的设计的更多相关文章

  1. jQuery 源码解析一:jQuery 类库整体架构设计解析

    如果是做 web 的话,相信都要对 Dom 进行增删查改,那大家都或多或少接触到过 jQuery 类库,其最大特色就是强大的选择器,让开发者脱离原生 JS 一大堆 getElementById.get ...

  2. 第十七章:jQuery类库

    javascript的核心API设计的很简单,但由于浏览器之间的不兼容性,导致客户端的API过于复杂.IE9的到来缓解了这种情况.然而使用javascript的框架或者工具类库,能简化通用操作,处理浏 ...

  3. BootStrap入门教程 (四) :JQuery类库插件(模态窗口,滚动监控,标签效果,提示效果,“泡芙”效果,警告区域,折叠效果,旋转木马,输入提示)

    上讲回顾:Bootstrap组件丰富同时具有良好可扩展性,能够很好地应用在生产环境.这些组件包括按钮(Button),导航(Navigation),缩略图( thumbnails),提醒(Alert) ...

  4. 基于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 ...

  5. Atitit.json类库的设计与实现 ati json lib

    Atitit.json类库的设计与实现 ati json lib 1. 目前jsonlib库可能有问题,可能版本冲突,抛出ex1 2. 解决之道:1 2.1. 自定义json解析库,使用多个复合的js ...

  6. 判断jquery类库是否加载,如未加载则加载。

    本人所有文章使用到的东西均在"渭南电脑维修网"网站中得以实现和应用,还请大家参考. 抄写别人网站的同时,N多不同的网站,势必有N多的css.javascript引用文件都会重复引用 ...

  7. JavaScript : 零基础打造自己的jquery类库

    写作不易,转载请注明出处,谢谢. 文章类别:Javascript基础(面向初学者) 前言 在之前的章节中,我们已经不依赖jQuery,单纯地用JavaScript封装了很多方法,这个时候,你一定会想, ...

  8. 浅谈 jQuery 核心架构设计

    jQuery对于大家而言并不陌生,因此关于它是什么以及它的作用,在这里我就不多言了,而本篇文章的目的是想通过对源码简单的分析来讨论 jQuery 的核心架构设计,以及jQuery 是如何利用javas ...

  9. 谈一谈jQuery核心架构设计(转)

    jQuery对于大家而言并不陌生,因此关于它是什么以及它的作用,在这里我就不多言了,而本篇文章的目的是想通过对源码简单的分析来讨论 jQuery 的核心架构设计,以及jQuery 是如何利用javas ...

随机推荐

  1. UIAccessibilityElement

    UIaccessibilityElement类封装的项目信息对一些特殊的人可见,默认情况下是不可访问的.例如,一个图标或文字图像不会自动访问,因为它没有继承的UIView(或UIControl).一个 ...

  2. PHP Yii2.0(一):环境搭建 & 问题集锦

    第一节 简单认识版本的异同 (1)版本说明 在安装和使用之前,我们需要知道 PHP Yii 有两个不同的版本(Yii 1.*或者Yii 2.*),这两个版本的目录结构不一样,其具体使用方式差异较大,因 ...

  3. 封印术:shadow dom

    置顶文章:<纯CSS打造银色MacBook Air(完整版)> 上一篇:<鼠标滚动插件smoovejs和wowjs> 作者主页:myvin 博主QQ:851399101(点击Q ...

  4. 『开源』仿SQLServer山寨一个 跨数据库客户端

    002 Laura.SqlForever项目简单介绍 相关文章 <『练手』001 Laura.SqlForever架构基础(Laura.XtraFramework 的变迁)> <『练 ...

  5. 『随笔』Socket 链接 必须 上下行 同时使用

    结论: > Socket 理论上 支持 只上行,或者 只下行. > 心跳包 必须是 上下行的 —— 心跳包请求(上行) - 心跳包响应(下行). > 如果 长时间 只有单向链接(只发 ...

  6. 自己编写redis客户端[deerlet-redis-client],分享与招募。

    引言 最近工作上有需要使用redis,于是便心血来潮打算自己写一个Java客户端.经过两天的努力,目前该客户端已经基本成型.不过可惜的是,由于redis的命令众多,因此LZ还需要慢慢扩展它去支持更多的 ...

  7. AngularJS开发指南12:AngularJS的模板,CSS,数据绑定详解

    模板 AngularJS模板是一种声明式的规则.它包含了模型和控制器的信息,最后会被渲染成用户在浏览器中看到的视图.它是静态的DOM,包含HTML,CSS和AngularJS指定的元素和属性.Angu ...

  8. AngularJS - 路由入门

    我们有很多方法让一个视图随着用户的操作进行变化. 但是,只是单单一个视图就要满足所有的需求会让代码变得非常复杂. 也许我们可以使用ng-include来引用各种模板,但这只限于部分场景. 于是我们可以 ...

  9. 团队项目NABCD模型的需求分析

    团队项目NABCD模型的需求分析 NABCD模型的介绍 Need(需求)-现在市场上未被满足但又急需满足的客户需求是什么?Approach(方法)-要满足这种需求,我能够提出什么独特的方法吗?Bene ...

  10. NABCD模型需求分析

    仓库管理系统的NABCD模型 N-Need仓库管理是与我们日常生活息息相关的问题,随着改革开放的不断深入,经济飞速的发展,企业要想生存.发展,要想在激烈的市场竞争中立于不败之地,没有现代化的管理是万万 ...