jQuery1.8.2源码解析之jQuery.Callbacks,已经在代码中添加了详细的注释,有需要的朋友看下边的代码就可以了。这样在使用时就方便了许多。
代码:
// String to Object options format cache
// 是对option的一个缓存,避免每次都要createOptions
// 每一个option类似这样
// {
// memory : true
// ,once : true
// ,...
// }
var optionsCache = {};
// Convert String-formatted options into Object-formatted ones and store in cache
function createOptions( options ) {
var object = optionsCache[ options ] = {};
jQuery.each( options.split( core_rspace ), function( _, flag ) {
object[ flag ] = true;
});
return object;
}
/*
* Create a callback list using the following parameters:
*
* options: an optional list of space-separated options that will change how
* the callback list behaves or a more traditional option object
*
* By default a callback list will act like an event callback list and can be
* "fired" multiple times.
*
* Possible options:
*
* once: will ensure the callback list can only be fired once (like a Deferred)
*
* memory: will keep track of previous values and will call any callback added
* after the list has been fired right away with the latest "memorized"
* values (like a Deferred)
*
* unique: will ensure a callback can only be added once (no duplicate in the list)
*
* stopOnFalse: interrupt callings when a callback returns false
*
*/
jQuery.Callbacks = function( options ) {
// Convert options from String-formatted to Object-formatted if needed
// (we check in cache first)
// options也可以是一个对象
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用来存储参数信息(此时函数列表已经处于firing状态,必须将其他地方调用fire时的参数存储,之后再至此执行fire
stack = !options.once && [],
// Fire callbacks
fire = function( data ) {
// 如果设置memory,那么每一次fire的数据将会被保存在memory中,作为下次调用add时参数
memory = options.memory && data;
fired = true;
firingIndex = firingStart || 0;
// 重置fireStarting为0,因为add操作(memory)可能改变它
firingStart = 0;
firingLength = list.length;
firing = true;
for ( ; list && firingIndex < firingLength; firingIndex++ ) {
// 如果设置了stopOnFalse参数,那么当函数列表中有某个函数返回false时,停止后面函数的执行,并且取消memory(如果设置了)
if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
memory = false; // To prevent further calls using add
break;
}
}
firing = false;
if ( list ) {
// 如果stack存在,那么将之前存储的第一个参数取出,继续fire,直到stack为空
if ( stack ) {
if ( stack.length ) {
fire( stack.shift() );
}
// 如果stack不存在(即设置了once)
// 那么如果设置了memory,那么将之前函数列表清空,也就是说memory还在,add操作可以处罚函数立即执行
} else if ( memory ) {
list = [];
} else {
self.disable();
}
}
},
// Actual Callbacks object
// 实际返回的Callbacks对象
self = {
// Add a callback or a collection of callbacks to the list
// 添加函数或者函数集(数组或者伪数组)到函数列表中去
add: function() {
if ( list ) {
// First, we save the current length
var start = list.length;
(function add( args ) {
jQuery.each( args, function( _, arg ) {
var type = jQuery.type( arg );
// 如果arg是函数
// 如果设置unique,则在list中查找是否函数已存在,若存在忽略掉,否则push进list
// 如果没有设置unique,则直接push进list
if ( type === "function" && ( !options.unique || !self.has( arg ) ) ) {
list.push( arg );
// 如果arg是数组或者伪数组,则递推push进list
} else if ( arg && arg.length && type !== "string" ) {
// Inspect recursively
// 递归(成员为函数集)
add( arg );
}
});
})( arguments );
// Do we need to add the callbacks to the
// current firing batch?
// 在添加函数(集)时
// 如果函数列表正在依次执行回调函数(即firing状态),在某一个callback中执行add(fn)操作
// 那么立即修改fireLength以便fire时函数列表能够执行到刚添加的函数(集)
if ( firing ) {
firingLength = list.length;
// With memory, if we're not firing then
// we should call right away
// 如果不是firing状态,并且设置了memory(肯定是在fired状态时才会执行这一步,因为memory是在fire一次后才会被负值)
// 此时memory已经是上次fire是传递的参数,那么将会直接执行刚添加的函数集,而无需fire
} else if ( memory ) {
firingStart = start;
fire( memory );
}
}
return this;
},
// Remove a callback from the list
// 从函数列表中删除函数(集)
remove: function() {
if ( list ) {
jQuery.each( arguments, function( _, arg ) {
var index;
// while循环的意义在于借助于强大的jQuery.inArray删除函数列表中相同的函数引用(没有设置unique的情况)
// jQuery.inArray将每次返回查找到的元素的index作为自己的第三个参数继续进行查找,直到函数列表的尽头
// splice删除数组元素,修改数组的结构
while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
list.splice( index, 1 );
// Handle firing indexes
// 在函数列表处于firing状态时,最主要的就是维护firingLength和firgingIndex这两个值
// 保证fire时函数列表中的函数能够被正确执行(fire中的for循环需要这两个值)
if ( firing ) {
if ( index <= firingLength ) {
firingLength--;
}
if ( index <= firingIndex ) {
firingIndex--;
}
}
}
});
}
return this;
},
// Control if a given callback is in the list
// 判断函数列表只能够是否包含给定的fn
has: function( fn ) {
return jQuery.inArray( fn, list ) > -1;
},
// Remove all callbacks from the list
// 清空函数列表
empty: function() {
list = [];
return this;
},
// Have the list do nothing anymore
// 使函数列表作废(不能再做任何事情)
disable: function() {
list = stack = memory = undefined;
return this;
},
// Is it disabled?
// 判断是否函数列表是否disabled
disabled: function() {
return !list;
},
// Lock the list in its current state
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
// 和fire相似,不相同的是fireWith可以给定上下文参数
// fire中就是调用fireWith中的context就是this(函数列表对象self)
fireWith: function( context, args ) {
args = args || [];
args = [ context, args.slice ? args.slice() : args ];
// 首先至少fire一次
// 如果执行过一次了(fired),那么若stack存在(即没有设置once),将上下文环境和参数存储,否则什么都不做
if ( list && ( !fired || stack ) ) {
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
// 判断是否函数列表fire过(哪怕只有一次)
fired: function() {
return !!fired;
}
};
return self;
};