项目地址:es6-jquery
一、callbacks模块作用
callbacks模块通过维护一个回调函数列表,来实现对回调函数地管理。其操作包括增加,触发,移除,清空,禁用,锁定等,是jquery.Defrred, jquery.ajax, jquery.ready等依赖的底层模块。
二、代码示例
let cbList = $.callbacks('once memory'); let fn = (args) => { alert(String(args)); } cbList.add( fn); cbList.fire('will fire');
三、API 详解
- add(fn || fnCollections)
增加一个或多个回调函数到内部回调函数列表中
- fire(args)
将给定的参数挨个传进回调函数列表中的函数,并执行
回调函数执行的上下文是当前callbacks
- fireWith(context, args)
将给定的参数挨个传进回调函数列表中的函数,并执行
回调函数执行的上下文是当前传入的context
- disable
禁用任何添加和触发操作,删除回调函数列表,清空记忆值,清空参数记忆数组,终止回调函数中所有操作
- lock
禁用callbacks的触发操作
如果当前不是memory模式且回调函数列表并没有在执行,则相当于disable操作
否则,只会禁用触发操作,后续的添加操作不会受影响
- remove(fn)
从回调函数列表中移除fn
- empty()
清空回调函数列表
- has(fn)
判断fn是否在当前回调函数列表中,如果fn为空,则判断当前回调函数列表是否为空
- locked()
判断当前回调函数列表是否处于锁定状态
- disabled()
返回当前回调函数列表的禁用状态
四、源码分析
- 整体结构
/* 1. 工具函数 2. 将以空格为分隔的字符串转为对象 */ function createOptions(options) { } let callbacks = function (options) { // 获取当前传入的options值 options = createOptions(options) || {}; /* 定义下面函数需要的各种变量 +. list 回调函数列表 +. firing 当前回调函数是否在执行 +. fired 当前回调函数列表是否至少已经执行过一次 +. locked 当前回调函数列表是否被锁定 +. disabled 当前回调函数列表是否被禁用 +. queue 存储回调函数列表触发时传入的参数,是个二维数组,每次传入[context, args] +. memory 这个值标示queue的值如何处理,值存在多种情况 > + options.memory不存在,memory为false > + 回调函数执行返回false且存在stopOnFalse标示时 > + 禁用状态下 memory值为空字符串 > + 锁定状态下,如果不存在memory标示,且回调函数列表并没有在执行,此时memory为空字符串 > + 其它情况 memory = [context, args], 用来获取queue中存储的记忆参数 */ // 定义一个对象用来存储callbacks的各种方法,如下 let self = { add: function () {}, fire: function () {}, fireWith: function () {}, remove: function () {}, empty: function () {}, has: function () {}, lock: function () {}, disable: function () {}, disabled: function () {} } return self; }
- 核心工具函数
fire 具体触发过程
- 状态设置
/* 设置当前回调列表的锁定状态 表示当前回调列表已经触发 */ locked = locked || options.once; fired = firing = true;
- 触发
// 当数组长度大于0时执行for循环体,如果数组长度小于等于0,将fireingIndex重置为-1 for(; queue.length; firingIndex = -1 ) { // 从queue中取出第一个值并将数组的长度减一 memory = queue.shift(); // ++firingIndex相当于从list[0]开始执行 while (++firingIndex < list.length) { // 如果回调函数执行返回false,会根据传入的标识进行处理 if (list[firingIndex].apply(memory[0], memory[1]) === false && options.stopOnFalse) { // 如果传入的标识含有stopOnFalse,那么会将memory设置为false // 同时将firingIndex 设置为数组最后一位 memory = false; firingIndex = list.length -1; } } }
- 触发之后,对回调函数列表进行设置
if (!options.memory) { memory = false; } if (locked) { if (memory) { list = []; } else { list = ''; } }
- 函数详解
(1) add 增加一个或者多个函数到回调函数列表中
如果是在memory模式下,增加的函数会立即执行,其执行的上下文为memory[0],执行的参数是memory[1]
- 追加函数到函数列表之前
/* 如果在memory模式下且回调函数列表没有被触发,则设定正在触发的回调函数索引为 回调函数列表长度减一,并将上一次触发的参数存入queue */ if (memory && !firing) { firingIndex = list.length - 1; queue.push(memory); }
- 追加函数到函数列表
// 使用一个立即执行函数来进行函数追加 (function (args) { // 分两种情况 // 1. 如果args是函数,当前是非唯一模式,则直接添加;如果是唯一模式, // 则判断args是否存在在当前函数列表中,如果不存在就添加它 // 2. 如果args是数组,则继续递归调用add进行深层遍历 })(arguments)
- 追加函数到函数列表之后
// 如果在memory模式下且当前回调函数列表没有在执行,就调用fire方法 if (memory && !firing) { fire(); }
(2) fireWith
- 参数收集
let fireWith: function (context, args) { if (!locked) { // 如果回调函数列表不是处于锁定或禁用状态 // 收集参数 let args = args || []; args = [context, args.slice ? args.slice : []]; if (queue) { // 如果queue存在,则将收集的参数数组传入queue queue.push([ context, args); } // 如果当前回调列表没有正在执行,则触发回调函数列表执行 if (!firing) { fire(); } } return this; }
(3) fire(args)
- 直接在自身作用域环境下触发
// fire实际上是fireWith调用的一种特殊情况, // 此时context为回调管理对象本身 let fire = function (args) { fireWith(this, args); return this; }
(4) lock
/* 1.设置locked和queue等于空数组 2. 这里有个疑问为什么不直接设置locked为true呢,其实空数组就是等于true 3. 如果memory为false或者为空,且当前回调函数列表尚没有执行,则彻底删除 回调函数列表, 设置memory等于空字符串。 */ locked = queue = []; if (!memory && !firing) { list = memory = ''; } return this;
(5) disable()
locked = queue; list = memory = ''; return this;
(6) locked()
return !!locked
(7) disabled()
/* 判断列表是否禁用 */ return !list
(8) empty()
// 清空列表 if (list) { list = []; }
(9) has(fn)
// 判断函数是否在回调函数列表,如果fn为空,则判断该函数列表是否长度大于0 // $.inArray(data, list, index) 返回某个数据在列表中的位置 类似于indexOf return fn ? $.inArray(fn, list) > -1 ? list.length > 0;
(10) remove(fn)
let remove = function (...fns) { let index = null; // 遍历参数 $.each(fns, function(_, fn) { while (index = ($.inArray(fn, list, index)) > -1 ) { list.splice(index, 1); if (index <= firingIndex) { firingIndex--; } } }) }