jQuery 无 new 创建实例
extend 方法
$() 选择器的封装
Callback API
Deferred API
jQuery 事件绑定原理
开始
本文仅对 jQuery 基本的 API 及其原理进行分析,源代码一万多行并没有完整分析,仅作参考
jQuery 无 new 创建实例
jQuery 共享原型的设计思想,将 jQuery 原型对象共享,然后通过扩展实例方法属性以及添加静态属性以及静态方法实现 jQuery 的灵活扩展
实现方法:创建一个 jQuery 对象, 返回 jQuery 原型对象的 init 方法, 然后共享原型, 将 jQuery 挂载到 windows 上起别名$,
实现通过$来访问 jQuery 的构造函数.同理通过$.fn 来替代 jQuery.prototype
1 | // 立即调用 |
extend 方法
使用示例:
1 | // 任意对象扩展 |
使用 jQuery 时,用 extend 方法进行扩展
- 先判断深浅复制的情况以及复制的是什么类型的变量,
- 然后要复制到 target 上,target 有可能是 jQuery 本身或者用户外部定义的变量,如果只传入一个值,则是扩展 jQuery 本身,
target=this=$=jQuery
- 否则则是扩展用户定义的变量,
target=arguments[0]
,即传入的第一个变量 - 最后,再进行拷贝,把扩展拷贝到 target 上并返回
1 | // 扩展的方法 |
$() 选择器的封装
根据 jQuery 共享原型的设计,$()实际上调用的是 jQuery.prototype.init()
示例:
1 | // 传入字符串 |
分析:
根据$()传过来的 selector 不同的数据类型,分析不同数据类型的行为,有以下几种情况:
- 如果传过来的数据是字符串:那么要分析字符串是否是 HTML 标签,如果是 HTML 那么就通过正则提取关键字并创建一个 HTM 标签输出
- 如果传过来的数据是不是 html 元素,那么要通过 querySelectorAll 来查询过滤,如果可以查询到是 DOM 中的选择器,那么就遍历输出他的值
- 如果传过来的元素是 DOM 节点,直接返回
- 如果传过来的数据是一个对象方法,那么要通过$(document).read()方法,监听拦截 DOMContentLoaded 方法,改变对象方法的指针然后依次加入到数组中,输出
1 | /** |
而 init()中用到的 ready,merge,parseHTML,makeArray 方法则扩展在jQuery.extend({})
中
1 | jQuery.extend({ |
Callback API
$.callbacks
用于管理函数队列,通过 add 添加处理函数到队列中,通过 fire 去执行这些函数,$.callbacks
是在 jQuery 内部使用的,如为.ajax,$.Deffed 等组件提供基础功能函数
四种调用模式:
- once:函数队列只执行一次
- unique:往内部队列添加的函数保持唯一,不能重复添加
- stopOnFalse:内部队列里的函数是依次执行的,当某个函数的返回值是 false 时,停止继续执行剩下的函数
- memory:当参数队列 fire 一次过后,内部会记录当前 fire 的参数。当下次调用 add 的时候,会把记录的参数传递给新添加的函数并立即执行这个新添加的函数
原理:
- 首先是
add()
方法:将穿过来的 options 先把他们转为真数组,然后将数组遍历出来挑选出类型为”Function”的数据,将数据添加到一个空数组中,等待执行 fire()
其实就是把添加到队列中的方法依次按规则输出执行,需要一个中间件 fireWith 提供上下文- 实现 stopOnFalse,
fire()
在遍历方法的时候,如果结果为 false,来判定 options 是否有 stopOnFalse 参数,如果有立马退出 - 实现 once,定义一个参数来记录第一次执行
fire()
的方法,然后在调用执行fire()
这个方法判断是否传入有 once 参数,如果有,那么就不会再去执行fire()
方法 - 实现 memory,
add()
阶段要记录传入的 options 是否有 memory 这个参数,其次在执行fire()
的阶段记录它的 index 值 - 实现 unique,
add()
阶段进行判定 unique 和方法列表中是否有当前 function,判断通过才 push 到列表中
1 | /** |
Deferred API
Deferred 是 jQuery 提出的回调函数解决方案,主要依赖 Callbacks 回调,主要解决的问题是:当一个异步依赖于另一个异步请求的结果时,或者某个操作需要等另外几个操作都结束后才开始等
API:
$.Deferred()
生成一个 deferred 对象deferred.done()
指定操作成功时的回调函数deferred.fail()
指定操作失败时的回调函数.promise()
返回一个 Promise 对象来观察当某种类型的所有行动绑定到结合,排队与否还是已经完成deferred.resolve()
手动改变 deferred 对象的运行状态为”已完成”,从而立即触发done()
方法deferred.reject()
这个方法与deferred.resolve()
正好相反,调用后将 deferred 对象的运行状态变为”已失败”,从而立即触发fail()
方法$.when()
为多个操作指定回调函数deferred.then()
可以把done()
和fail()
合在一起写,参考 Promisedeferred.progress()
当 Deferred 延迟对象生成时,调用已添加的处理程序
实现原理
- 首先定义了 tuples 的数据结构,用来组装存储异步延迟三种不同状态信息的描述
- 然后定义一个 promise 用来封装 state,then,promise 对象
- 定义一个延迟对象 deferred = {};
- 遍历封装好的 tuples 数组队列,把 tuples 数组里第二个元素映射到对应的 Callbacks,将数组的第三个元素记录的最终状态信息给到 stateString,然后把数组第一个元素即延时对象的状态映射到 Callbacks 的 add 方法上,定义辅助方法
- 最后调用 Callbacks 的 fireWith 方法实现队列的回调
1 | /** |
jQuery 事件绑定原理
示例:
1 | // 多个事件绑定同一个函数: |
原理:
- jQuery 并没有将事件处理函数直接绑定到 DOM 元素上,而是通过 jQuery.event.add 存储在 cache 缓存对象上。
- 最后通过在 DOM 元素上通过 addEventListener/attachEvent 绑定事件。
- 当事件触发时,eventHandle 被执行,eventHandle 再去$.cache 中寻找曾经绑定的事件处理函数并执行
1 | /** |
结语
以上分析,谢谢阅读