jQuery封装了不少关于异步操做的方法,好比$.ajax
$.Deferred
等等,这些方法都是以Callbacks为基础开发的,Callbacks一个多用途的回调列表对象,提供了强大的的方式来管理回调函数列表,他能够管理一个列表,在你须要执行时候触发,触发时机由调用fire方法决定html
var cb = $.Callbacks();
cb.add(function(){
console.log('first function');
})
cb.add(function () {
console.log('second function');
})
cb.fire();
// first function
// second function
复制代码
$.Callbacks()返回一个Callbacks对象,你能够调用该对象的方法对函数列表进行操做,要实现这样的功能很简单,下面是一个简易的实现ajax
function Callbakcs () {
var list = []
return {
add: function (func) {
list.push(func);
},
fire: function () {
var args = [].slice.call(arguments);
list.forEach(function(item) {
item.apply(null, arguments);
})
}
}
}
复制代码
一样的功能,只不过jQuery支持了不一样的调用方式数组
在调用$.Callbacks的时候,咱们能够传入一个参数用来改变回调列表的行为缓存
// 支持两种传参方式,一种对象类的,一种字符串类的,可组合使用,传入字符串的时候会对字符串进行处理转换为对象
var cb = $.Callback("once unique");
var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g );
function createOptions( options ) {
var object = {};
jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) {
object[ flag ] = true;
} );
return object;
}
// 函数内部存储了一个options对象,最后会是相似{once: true, unique: true}这样的对象
复制代码
在函数内部咱们还能看到其余一些变量bash
var // 标记是否正在触发
firing,
// 缓存的上一次调用时候的参数和this指向
memory,
// 标记是否已经调用过fire
fired,
// 防止调用的标记
locked,
// 回调列表
list = [],
// 参数和this指向的列表
queue = [],
// 标记当前执行到哪一个回调
firingIndex = -1,
// 执行回调列表
fire = function () {}
// 暴露的对象
self = {}
复制代码
add方法主要会判断是不是unique、memory模式、unique遇到相同的回调函数回过滤掉,memory会检查fired是否为true,若是是会将memory里面的参数添加到queue队列中,最后调用fireapp
if ( memory && !firing ) {
firingIndex = list.length - 1;
queue.push( memory );
}
if ( memory && !firing ) {
firingIndex = list.length - 1;
queue.push( memory );
}
( function add( args ) {
jQuery.each( args, function( _, arg ) {
if ( isFunction( arg ) ) {
if ( !options.unique || !self.has( arg ) ) {
list.push( arg );
}
} else if ( arg && arg.length && toType( arg ) !== "string" ) {
// Inspect recursively
add( arg );
}
} );
} )( arguments );
复制代码
添加的是否用到了递归,由于支持了传入数组的方法异步
self里面会调用fireWith方法,咱们在调用fire的时候其实是调用的这个方法函数
fire: function() {
self.fireWith( this, arguments );
return this;
},
fireWith: function( context, args ) {
if ( !locked ) {
args = args || [];
args = [ context, args.slice ? args.slice() : args ];
// 降参数和this对象缓存到queue里面
queue.push( args );
if ( !firing ) {
fire();
}
}
return this;
},
复制代码
真正的fire会遍历取出queue里面的参数,缓存到memory里面,若是不是memory模式则会在后面清除掉,遍历list,用memory里的参数调用各个方法ui
for ( ; queue.length; firingIndex = -1 ) {
memory = queue.shift();
while ( ++firingIndex < list.length ) {
// 若是传入stopOnFalse,且返回false,当即中止遍历
if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false &&
options.stopOnFalse ) {
firingIndex = list.length;
memory = false;
}
}
}
复制代码
Callback部分的代码比较简单,固然这部分都是同步逻辑,callback是Deffered的基础,因此须要先明白一下原理this