Callbacks是JQ的一个回调对象,可以对回调进行统一的管理。而且还为Deferred延迟对象提供了基础功能。
一、举例:
function aa(){
alert(1);
}
function bb(){
alert(2);
}
setTimeout(function(){
aa();
},100);
bb();
以上代码执行顺序是bb(),然后执行aa(),但是如果我们想要aa()先执行,然后在执行bb()呢?当然我们可以把bb(),添加到setTimeout()中,但是,如果有大量的函数的话,这样会使代码变得臃肿,难于维护。
var ck= $.Callbacks();//Callbacks对象
function aa(){
alert(1);
}
function bb(){
alert(2);
}
ck.add(bb); //添加进list数组
setTimeout(function(){
aa();
ck.fire(); //触发
},100);
这样代码就会先执行aa(),然后执行bb();
二、.源码解析
1.$.Callbacks()
当执行$.Callbacks()时,我们看源码
jQuery.Callbacks = function( options ) {
// Convert options from String-formatted to Object-formatted if needed
// (we check in cache first)
options = typeof options === "string" ?
( optionsCache[ options ] || createOptions( options ) ) :
jQuery.extend( {}, options );
1.1我们发现Callbacks函数内部可以传参数,options的值有以下几种情况或者组合形式:
(1)once
fire()只触发一次
var ck= $.Callbacks("once");
function aa(){
alert(1);
}
function bb(){
alert(2);
}
ck.add(aa,bb);
ck.fire();
ck.fire();
以上只触发了一次fire()事件。
(2)memory
记忆功能,记住fire(),如果在fire()触发之后,再继续添加回调,那么直接执行函数。
var ck= $.Callbacks("memory");
function aa(){
alert(1);
}
function bb(){
alert(2);
}
ck.add(aa);
ck.fire();
ck.add(bb);
这里fire事件触发后,先执行了aa(),ck.add(bb)之后,bb()函数立即触发。如果,没有"memory",那么bb()是不会执行的
(3)unique
每个函数只会添加一次,不会有重复。
var ck= $.Callbacks("unique");
function aa(){
alert(1);
}
function bb(){
alert(2);
}
ck.add(aa);
ck.add(aa);
ck.fire();
这里只会执行一次aa(),如果没有unique的话,会执行两遍。
(4)stopOnFalse
当添加的函数中返回false时,中止fire事件。
var ck= $.Callbacks("stopOnFalse");
function aa(){
alert(1);
}
function bb(){
alert(2);
return false;
}
ck.add(bb);
ck.add(aa);
ck.fire();
bb()函数会执行,aa()不会执行。
1.2 optionsCache[ options ]与createOptions
// Convert String-formatted options into Object-formatted ones and store in cache
function createOptions( options ) {
var object = optionsCache[ options ] = {};
jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) {
object[ flag ] = true;
});
return object;
}
optionsCache是缓存对象。
createOptions函数会把 “memory once”这样的options入参变成:object.memory=true;object.once=true;这会在源码中用到。
2. add()
调用Callbacks函数返回:
return self;
我们看源码,发现self是一个对象。
// Actual Callbacks object
self = {
add: function() {},
remove: function() {},
fireWith:function(){},
......
}
}
self里面才是我们Callbacks调用的真实对象,我们下面看下self.add()方法:
add: function() {
if ( list ) { //list初始化为[],所以会进入if
// First, we save the current length
var start = list.length;
(function add( args ) { //这里使用了匿名自执行,并且传入add()方法的参数对象arguments,这里我对于为什么使用匿 名自执行感觉不太理解,感觉完全没有必要,使用了之后,参数的作用域链变长了,也不需要避免向全局作用域写参数???
jQuery.each( args, function( _, arg ) {//调用each方法,为args里面的每一个参数绑定一个匿名函数,匿名函数里 面的第一个参数 _ 是索引,第二个参数arg是具体的某一项的值或者引用
var type = jQuery.type( arg );
if ( type === "function" ) { //如果传进来的是函数进入if
if ( !options.unique || !self.has( arg ) ) { //如果在Callbacks没有传入unique或者列表list 中没有没有arg,进入if
list.push( arg );
}
} else if ( arg && arg.length && type !== "string" ) { //适用于ck.add(aa,bb,cc);的情况
// Inspect recursively
add( arg );
}
});
})( arguments );
if ( firing ) {
firingLength = list.length;
// With memory, if we're not firing then
// we should call right away
} else if ( memory ) { //如果Callbacks函数中传入了memory,那么后面再add进来的函数直接触发fire事件
firingStart = start;
fire( memory );
}
}
return this;
},
3.fireWith()和fire()
当执行ck.fire()时,我们先回触发self.fire()事件。
fire: function() {
self.fireWith( this, arguments );
return this;
},
而self.fire()触发的使self.fireWith()事件。
fireWith: function( context, args ) {
if ( list && ( !fired || stack ) ) { //stack = !options.once && [] 这个不需要解释了吧!没有once或者没有执行guofire事件就为true
args = args || [];
args = [ context, args.slice ? args.slice() : args ]; //context为self对象,args.slice数组拷贝
if ( firing ) { //如果这时正在执行fire事件,我们暂时把这些将要触发的函数,存到stack中。
stack.push( args );
} else { //否则,直接触发函数
fire( args );
}
}
return this;
},
self.fireWith()调用内部的fire函数
fire = function( data ) {
memory = options.memory && data; //有memory其有要执行的函数
fired = true; //fired标记置为true
firingIndex = firingStart || 0;
firingStart = 0;
firingLength = list.length;
firing = true; //正在触发fire事件
for ( ; list && firingIndex < firingLength; firingIndex++ ) {
if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {//data[0]就是self,执行函数list[index],如果执行的函数返回了false并且有stopOnFalse,进入if
memory = false; // To prevent further calls using add //把memory置为false,并且跳出循环,不再继续触发。
break;
}
}
firing = false;
if ( list ) {
if ( stack ) {//如果stack中存着函数引用,
if ( stack.length ) {
fire( stack.shift() );//触发stack存储的函数
}
} else if ( memory ) {如果有memory
list = [];
} else {
self.disable();//没有memory,后续不再触发fire函数
}
}
},