JQuery那点事 -- Callback
Callback(事件队列)
所谓的事件队列就是一个函数的集合对象,这个对象下有添加、删除、执行函数等等各种操作集合中函数的方法。
顺便说一下 在 JQuery
中 Promise
的实现也与事件队列密不可分,那么接下来从 jQuery
源码来看 Callback
是如何实现、应该如何去使用。
JQuery 中 Callback 的简单实现
以下代码为 jQuery 3.1.0
中,关于 Callbacks
源码中的一部分。经过我的一些修改,仅仅实现了主要的逻辑。
function Callbacks(){
var // 函数队列是否被执行的标识
fired,
// 函数队列,用于存放 function
list = [],
// 当前在函数队列中执行的函数的位置(index)
firingIndex = -1,
// 执行函数队列
fire = function() {
while (++firingIndex < list.length) {
list[firingIndex]();
}
},
// 调用 Callbacks 返回的对象
self = {
// 在函数队列中添加函数。
add: function(fun) {
if (list) {
list.push(fun);
}
return this;
},
// 从函数列表中去除函数
remove: function(fun) {
var index;
// 重复函数判断
while ((index = jQuery.inArray(fun, list, index)) > -1) {
list.splice(index, 1);
// 纠正正在执行的函数的位置信息
if (index <= firingIndex) {
firingIndex--;
}
}
return this;
},
// 移除所有的函数
empty: function() {
if (list) {
list = [];
}
return this;
},
// 执行函数队列
fire: function() {
fire();
return this;
},
};
return self;
}
调用 Callbacks() 就能生成一个简易版的事件队列,调用返回对象下的 fire 方法就能依次执行函数队列下的函数。
var callbacks = Callbacks();
var fun1 = function () {
console.log('fun1 start');
};
var fun2 = function () {
console.log('fun2 start');
};
callbacks.add(fun1).add(fun2);
callbacks.fire();
// console
// fun1 start
// fun2 start
简易版 Callbacks 的进一步升级
js 一个极其重要的特点就是可以使用不定数量的参数。所以可以将 add
方法改成下面这样子,更一步接近了 JQuery
中源码的样子:
add: function() {
// 添加函数,递归添加
( function add( args ) {
jQuery.each( args, function( _, arg ) {
if ( jQuery.isFunction( arg ) ) {
list.push( arg );
} else if ( arg && arg.length && jQuery.type( arg ) !== "string" ) {
// 对于 [function,function] 参数的添加
add( arg );
}
} );
} )( arguments );
return this;
}
接受参数形如 (fun,[fun,fun],fun) 。
callbacks.add(fun1, fun2, [fun3, fun4]);
同样的 remove
也可修改为:
remove: function() {
jQuery.each( arguments, function( _, arg ) {
var index;
// 重复函数判断
while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
list.splice( index, 1 );
// Handle firing indexes
// 纠正正在执行的函数的位置信息
if ( index <= firingIndex ) {
firingIndex--;
}
}
} );
return this;
}
接受参数形如 (fun,fun) 。
callbacks.remove(fun1, fun2);
功能性方法添加
当然在 jQuery
源码中还有一些功能性的方法。
- has 判断函数是否在函数队列中
has: function( fn ) {
return fn ?
jQuery.inArray( fn, list ) > -1 :
list.length > 0;
}
- empty 移除所有的函数
empty: function() {
if ( list ) {
list = [];
}
return this;
}
- disable && disabled | 无效这个 callbacks && 查看当前 callbacks 的状态
disable: function() {
list = "";
return this;
},
disabled: function() {
return !list;
}
升级 Callbacks 方法
经过上面这几个步骤,就可以大概拼凑出一个接近 JQuery
源码的 Callbacks
。
但是 jQuery
源码中 Callbacks
拥有更强大的功能,可以拥有 4 中状态:
- once: 只能被执行一次的函数队列
- memory: 执行后会保留执行的上下文环境和参数列表的函数队列。
- unique: 只能添加不同函数的函数队列。
- stopOnFalse: 只依次执行函数队列中的函数时,只要有函数报错,队列中的函数便不继续运行下去。能添加不同函数的函数队列。
这 4 中 Callbacks
的实现需要在上面我们拼凑出 Callbacks
上加上一些流程控制。具体的请看给出的 jQuery
源码中的注释(点击查看)。
注: jQuery
中的 Callbacks
允许两种声明方式:
// 方式1
jQuery.Callbacks( "once memory" )
// 方式2
jQuery.Callbacks( { once: true, memory: true } )