在回调函数列表内部,通过一个数组来保存回调函数,其他方法则围绕这个数组进行操作和检测。
回调函数列表支持添加、移除、触发、锁定和禁用回调函数,为jQuery.ajax()、jQuery.Deferred()和ready事件提供基础功能,我们也可以基于它编写新得组件。
// 代码行 3190——3201 var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g ); // Convert String-formatted options into Object-formatted ones // 工具函数,将字符串格式得标记转换为对象格式得标记。 function createOptions( options ) { var object = {}; // 对象object用于存储转换后得标记对象,其属性为标记字符串,属性值为true。 jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) { // 用空白符吧标记字符串分割为数组,然后遍历数组,为返回值object,添加单个标记,属性值一律为true。 object[ flag ] = true; } ); // 最后返回对象格式得标记 return object; } // 代码行:3203——3417 /* * 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 ) { // 参数options是可选得字符串,用以改变回调函数列表得行为。如果不传入参数options,则回调函数列表得行为类似于事件监听函数,能够被触发多次;如果传入参数options,则参数值可以是单个标记或多个标记得组合,多个标记之间用空格隔开。 // Convert options from String-formatted to Object-formatted if needed // (we check in cache first) // 解析字符串标记options为对象 // 如果options是字符串,调用工具函数createOptions(options)将标记字符串options解析为标记对象,否则调用jquery.extend()将options中的内容合并到一个空对象中 options = typeof options === "string" ? createOptions( options ) : jQuery.extend( {}, options ); // 声明局部变量,通过闭包机制引用 var // Flag to know if list is currently firing // 标记,以知道回调函数列表是否正在执行中 firing, // Last fire value for non-forgettable lists // memory得可能值和用途有些复杂,在分析工具fire(context,args)时详细分析 memory, // Flag to know if list was already fired // 标记,以知道list是否已被触发 fired, // Flag to prevent firing // 标记,防止firing locked, // Actual callback list // 存放回调函数得数组 list = [], // Queue of execution data for repeatable lists // 在可重复触发、正在执行得列表上,重复触发时,将上下文和参数放入数组queue中 queue = [], // Index of currently firing callback (modified by add/remove as needed) // 当前正在执行得回调函数得下标 firingIndex = -1, // Fire callbacks // 实际触发回调函数得工具函数 // 工具函数fire()调用数组list中得回调函数。该函数通过闭包机制引用数组list fire = function() { // Enforce single-firing locked = locked || options.once; // Execute callbacks for all pending executions, // respecting firingIndex overrides and runtime changes fired = firing = true; for ( ; queue.length; firingIndex = -1 ) { // 变量memory的初始值为undefined,表示当前回调函数列表未被触发过。 // 在memory模式下,变量memory的值为[context,args],间接地表示回调函数列表已经被触发过。 // 在非memory模式下,变量memory的值为true,间接地表示回调函数列表已经被触发过;如果同时是once模式,则不再执行任何回调函数。 // 在stopOnFalse模式下,如果某个回调函数返回了false,则变量memory的值为true,间接地表示回调函数列表已经被触发过。 memory = queue.shift(); while ( ++firingIndex < list.length ) { // Run callback and check for early termination // 执行回调函数list[firingIndex],如果返回值是false,并且当前回调函数列表是stopOnFalse模式,则变量memory则赋值为false,并且停止执行后续得其他回调函数,也间接表示了当前回调函数列表已经被触发过。 if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false && options.stopOnFalse ) { // Jump to end and forget the data so .add doesn't re-fire firingIndex = list.length; memory = false; } } } // Forget the data if we're done with it if ( !options.memory ) { memory = false; } firing = false; // Clean up if we're done firing for good if ( locked ) { // Keep an empty list if we have data for future add calls // 如果我们有用于将来添加调用的数据,请保留一个空列表 if ( memory ) { list = []; // Otherwise, this object is spent // 否则,此对象将被使用 } else { list = ""; } } }, // Actual Callbacks object // 回调函数列表,方法jQuery.callbacks(flags)得返回值。 self = { // Add a callback or a collection of callbacks to the list // 添加一个或一组回调函数到回调函数列表中 add: function() { if ( list ) { // If we have memory from a past run, we should fire after adding // 如果我们有过去运行时的内存,则应该在添加之后触发 if ( memory && !firing ) { // 在开始添加回调函数前,先备份数组list的长度,该值也是回调函数的插入位置 firingIndex = list.length - 1; queue.push( memory ); } // 调用工具函数add()添加回调函数 ( 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 ); // 在memory模式下,如果回调函数列表未在执行中,并且已经被触发过,则修正起始下标为回调函数的插入位置,然后调用工具函数fire()立即执行添加新的回调函数。 if ( memory && !firing ) { fire(); } } // 返回当前回调函数列表,以保持链式语法 return this; }, // Remove a callback from the list // 从回调函数列表中移除一个或一组回调函数 // 方法callbacks.remove()用于从回调函数列表中移除一个或一组回调函数,移除前修正当前下标firingIndex,确保移除的同时不会漏执行回调函数。 remove: function() { // 遍历待移除的回调函数数组,在循环体内嵌套遍历已有的回调函数数组,如果检查到某个待移除回调函数与某个已有的回调函数完全相等,则从已有的回调函数数组中移除它。 jQuery.each( arguments, function( _, arg ) { var index; while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { // 调用数组原型方法splice(),从匹配下标处开始移除1个元素, list.splice( index, 1 ); // Handle firing indexes // 如果待移除函数的下标小于正在执行回调函数的下标firingIndex,即待移除的回调函数已经执行,则修正firingIndex减1,以确保不会漏执行回调函数。 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.length > 0; }, // Remove all callbacks from the list // 清空列表,移除回调函数列表中得所有回调函数 empty: function() { if ( list ) { list = []; } return this; }, // Disable .fire and .add // Abort any current/pending executions // Clear all callbacks and values // 禁用回调函数列表,不在做任何事情 disable: function() { // 设置变量locked、queue为空数组,list,memory为“” // 在方法callbacks.add()、callback.remove()中,如果变量list可以转换为false,则忽略本次调用,即无法添加、移除回调函数;在方法callback.fireWith()中,如果变量locked的状态可以转换为false,则忽略本次调用,如果可以转换为true,才有可能触发回调函数。 locked = queue = []; list = memory = ""; return this; }, // 判断回调函数列表是否已被禁用 disabled: function() { return !list; }, // Disable .fire // Also disable .add unless we have memory (since it would have no effect) // Abort any pending executions // 锁定回调函数列表 lock: function() { locked = queue = []; if ( !memory && !firing ) { list = memory = ""; } return this; }, // 判断回调函数列表是否已被锁定 locked: function() { return !!locked; }, // Call all callbacks with the given context and arguments // 使用指定得上下文和参数触发回调函数列表中得所有回调函数 fireWith: function( context, args ) { // 先检测变量locked的状态,如果可以转换为false,则忽略本次调用,如果可以转换为true,才有可能触发回调函数。 if ( !locked ) { // 如果未传入args,则使用默认值[] args = args || []; args = [ context, args.slice ? args.slice() : args ]; // 在可重复触发、正在执行得列表上,重复触发时,将上下文和参数放入数组queue中 queue.push( args ); // 回调函数列表不正在执行中,则调用工具函数fire()执行所有回调函数 if ( !firing ) { fire(); } } return this; }, // Call all the callbacks with the given arguments // 使用指定得参数调用回调函数列表中得所有回调函数 fire: function() { // 方法callbacks.fire()通过调用callbacks.fireWith(context,args)实现,调用时指定上下文为当前回调函数列表。 self.fireWith( this, arguments ); return this; }, // To know if the callbacks have already been called at least once // 判断回调函数列表是否至少执行过一次 fired: function() { // 方法callbacks.fired()使用逻辑非运算符(!)对变量fired两次取反,转换未布尔值并返回。 return !!fired; } }; // 返回回调函数列表self return self; };