JQuery并没有简单的使用一个Array来存储回调函数,而是通过JQuery.Callbacks(options)返回一个self对象,此对象能够动态的add,remove和fire回调函数队列.此函数须要说明的是options參数,它是一个string,这个string由四个參数随意组合而成

options:


once:回调函数仅仅运行一次


memory:调用add时触发回调函数使用fire时传入的參数.


unique:回调函数仅仅能被加入到队列一次


stopOnFlase:有一个回调函数返回false,停止运行以下的回调函数.





JQuery.Callbacks(options)内的变量依据不同传參有不同的意思,这些变量由闭包引用扩大其生命周期,以在外部改变其值,下面进行说明


memory:假设options不包括'memory'字符串,memeory为false,其它为上一次fire(args)的參数.


fired:回调函数列表是否被触发过.


firing:回调函数列表是否正在被触发.为何要这个參数呢?由于假设在回调函数内调用了add,remove,fire 方法时会改变回调函数列表的状态(開始触发的位置,触发长度等等,这些是控制要运行回调函数列表中哪些函数的标志),此时其回调函数函数列表正在处于触发中,必须能够及时反映(修正触发位置,触发长度等等)回调函数列表的变化,以便在回调函数运行的循环中下一次迭代能够正确的触发回调函数列表.


firingStart:回调函数列表运行的開始位置.


firingLength:再运行几个回调函数


firingIndex:正在触发的函数在回调函数列表中的位置


list:存放回调函数


stack:假设在回调函数中调用fire或fireWith方法,将fire或fireWith传入的上下文和參数先入栈,稍后再用栈中的參数来作为參数运行回调函数中调用的fire或fireWidth。

注:不解的是走到stack.push分支时是在回调函数里调用fire或fireWidth,但这样会导致死循环,这个stack究竟怎么回事?还请明确的同学告知我一下。!

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 );

var // Last fire value (for non-forgettable lists)
memory,
// Flag to know if list was already fired
fired,
// Flag to know if list is currently firing
firing,
// First callback to fire (used internally by add and fireWith)
firingStart,
// End of the loop when firing
firingLength,
// Index of currently firing callback (modified by remove if needed)
firingIndex,
// Actual callback list
list = [],
// Stack of fire calls for repeatable lists
// 假设没有配置once,stack为数组
// stack存放反复时fire參数
stack = !options.once && [],
// Fire callbacks
fire = function( data ) {
//memory 为false或fire时传入的參数
memory = options.memory && data;
fired = true;
//当前触发位置
firingIndex = firingStart || 0;
//触发回调函数队列的起始位置
firingStart = 0;
//待触发的回调函数队列长度
firingLength = list.length;
//正在触发标识设置为true
//仅仅有在Callbacks的回调函数里此值才会为true
firing = true;
for ( ; list && firingIndex < firingLength; firingIndex++ ) {
//若配置了stopOnFalse当回调函数队列中函数返回值为false
if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
//memory设置为false
memory = false; // To prevent further calls using add
//不再运行后面的回调函数
break;
}
}
//回调函数运行结束
firing = false;
if ( list ) {
//未配置once
if ( stack ) {
//运行在回调函数里调用的fire
if ( stack.length ) {
fire( stack.shift() );
}
//清空回调函数列表,但此callbacks仍可用
} else if ( memory ) {
list = [];
//让闭包的那些引用能够销毁
} else {
self.disable();
}
}
},
// Actual Callbacks object
self = {
// Add a callback or a collection of callbacks to the list
add: function() {
if ( list ) {
// First, we save the current length
// 将加入回调函数之前将当前callbacks的长度设为运行開始
var start = list.length;
( function add( args ) {
jQuery.each( args, function( _, arg ) {
var type = jQuery.type( arg );
if ( type === "function" ) {
// 假设未设置unique或者回调函数列表里没有此函数
// 在回调函数队列里加上此函数
if ( !options.unique || !self.has( arg ) ) {
list.push( arg );
}
// 假设arg是个类数组,再递归add
} else if ( arg && arg.length && type !== "string" ) {
// Inspect recursively
add( arg );
}
});
})( arguments );
// Do we need to add the callbacks to the
// current firing batch?
//在正在触发的回调函数中调用add方法会走此分支
if ( firing ) {
//立马反应要触发回调函数长度的改变
firingLength = list.length;
// With memory, if we're not firing then
// we should call right away
} else if ( memory ) {
//改变触发的開始位置为未加入前的回调队列长度
firingStart = start;
fire( memory );
}
}
return this;
},
// Remove a callback from the list
remove: function() {
if ( list ) {
jQuery.each( arguments, function( _, arg ) {
//arg所在list的索引
var index;
while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
//删除
list.splice( index, 1 );
// Handle firing indexes
//假设在回调函数里调用remove
if ( firing ) {
//要删除的回调函数是还没触发的
if ( index <= firingLength ) {
//触发的长度减一
firingLength--;
}
//要删除的是已经触发过的函数
if ( index <= firingIndex ) {
//正要触发索引减一
firingIndex--;
}
}
}
});
}
return this;
},
// Check if a given callback is in the list.
// If no argument is given, return whether or not list has callbacks attached.
has: function( fn ) {
return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length );
},
// Remove all callbacks from the list
empty: function() {
list = [];
firingLength = 0;
return this;
},
// Have the list do nothing anymore
//是回调函数列表功能全然失效
disable: function() {
list = stack = memory = undefined;
return this;
},
// Is it disabled?

disabled: function() {
return !list;
},
// Lock the list in its current state
//锁住当前的状态,不会运行在回调函数里调用fire或fireWidth
lock: function() {
stack = undefined;
if ( ! memory ) {
self.disable();
}
return this;
},
// Is it locked?
locked: function() {
return !stack;
},
// Call all callbacks with the given context and arguments
fireWith: function( context, args ) {
//没有被触发或者能够反复触发
if ( list && ( !fired || stack ) ) {
args = args || [];
args = [ context, args.slice ? args.slice() : args ];
//?事实上这里有一点不解,走到这个分
//是在在回调函数里调通fire或fireWidth,但这样会导致死循环
if ( firing ) {
stack.push( args );
} else {
fire( args );
}
}
return this;
},
// Call all the callbacks with the given arguments
fire: function() {
self.fireWith( this, arguments );
return this;
},
// To know if the callbacks have already been called at least once
fired: function() {
return !!fired;
}
};

return self;
};