jQuery.Callbacks

var optionsCache = {};

// Convert String-formatted options into Object-formatted ones and store in cache
function createOptions( options ) {
	var object = optionsCache[ options ] = {};
	jQuery.each( options.match( core_rnotwhite ) || [], 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 = 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
		stack = !options.once && [],
		// Fire callbacks
		fire = function( data ) {
			memory = options.memory && data;
			fired = true;
			firingIndex = firingStart || 0;
			firingStart = 0;
			firingLength = list.length;
			firing = true;
			for ( ; list && firingIndex < firingLength; firingIndex++ ) {
				if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
					memory = false; // To prevent further calls using add
					break;
				}
			}
			firing = false;
			if ( list ) {
				if ( stack ) {
					if ( stack.length ) {
						fire( stack.shift() );
					}
				} 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
					var start = list.length;
					(function add( args ) {
						jQuery.each( args, function( _, arg ) {
							var type = jQuery.type( arg );
							if ( type === "function" ) {
								if ( !options.unique || !self.has( arg ) ) {
									list.push( arg );
								}
							} else if ( arg && arg.length && type !== "string" ) {
								// Inspect recursively
								add( arg );
							}
						});
					})( arguments );
					// Do we need to add the callbacks to the
					// current firing batch?
					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 ) {
						var index;
						while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
							list.splice( index, 1 );
							// Handle firing indexes
							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
			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 ];
					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;
};


普通用法:



once:fire一次,清空之前的add函数,并且锁住(后续add和fire无效)。



memory:执行fire后面所添加的add



 unique:过滤


stopOnFalse:函数返回值false中止后续执行



特殊形式:可以创建的时候组合以上参数

var cb = $.Callbacks('once memory unique');


代码分析:

1、从$.Callbacks()创建的,这是入口


var optionsCache = {};

// Convert String-formatted options into Object-formatted ones and store in cache
function createOptions( options ) {
   var object = optionsCache[ options ] = {};
   jQuery.each( options.match( /\S+/g ) || [], function( _, flag ) {
      object[ flag ] = true;
   });
   return object;
}


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

options为传入的参数,如果存在则获取,不存在则创建,如果传入的是"once momery",则

options = {

    once: true,

    momery: true

};

然后返回self


2、cb.add()

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 );
            if ( type === "function" ) {
               if ( !options.unique || !self.has( arg ) ) {
                  list.push( arg );
               }
            } else if ( arg && arg.length && type !== "string" ) {
               // Inspect recursively
               add( arg );
            }
         });
      })( arguments );
      // Do we need to add the callbacks to the
      // current firing batch?
      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;
}
list:add添加的函数放在里面

start:list遍历的起始索引

(function add(){}):匿名函数自执行,作用是list添加函数

 !options.unique || !self.has( arg )
    根据unique过滤已经传入的函数

firing:如果add的函数正在执行,更新list数组的长度

如果有memory参数,调用fire方法

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


3、cb.fire()方法

fire: function() {
   self.fireWith( this, arguments );
   return this;
},
执行fireWith

fireWith: function( context, args ) {
   if ( list && ( !fired || stack ) ) {
      args = args || [];
      args = [ context, args.slice ? args.slice() : args ];
      if ( firing ) {
         stack.push( args );
      } else {
         fire( args );
      }
   }
   return this;
},

list:遍历执行的数组

fired:是否执行过,如果没有cb.fire()则为false,有过cb.fire()则为true

stack:堆,如果之前add的函数还在执行中,则等执行完毕后在执行stack里面的,创建有once参数就是false。

firing:之前add的函数是否在执行中

fire:之前申明的函数,主要。

第一次执行:

list = []

fired = undefined,

stack = !options.once && [],  //创建有once参数则为false,没有once参数则为[]

由判断参数可见,第一次绝对可进if

args进行了包装,后续的fire()执行,是可以传入参数的,如:fire(1,2,3),在之前的add函数执行传入1,2,3。

后续是否进入if,根据list和stack来判断

if ( list ) {
   if ( stack ) {
      if ( stack.length ) {
         fire( stack.shift() );
      }
   } else if ( memory ) {
      list = [];
   } else {
      self.disable();
   }
}
这是fire函数的最后一个判断

stack为[],则递归调用stack数组,最后为0停止。

stack为false(once),如果有memory参数,list为空了,只要在add之后,list才有值,fire才能调用

stack为false(once),如果没有memory参数,调用disable()方法锁住,后续add,fire无用,调用empty方法解锁。

disable: function() {
   list = stack = memory = undefined;
   return this;
}

4、调用fire方法

fire = function( data ) {
   memory = options.memory && data;
   fired = true;
   firingIndex = firingStart || 0;
   firingStart = 0;
   firingLength = list.length;
   firing = true;
   for ( ; list && firingIndex < firingLength; firingIndex++ ) {
      if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
         memory = false; // To prevent further calls using add
         break;
      }
   }
   firing = false;
   if ( list ) {
      if ( stack ) {
         if ( stack.length ) {
            fire( stack.shift() );
         }
      } else if ( memory ) {
         list = [];
      } else {
         self.disable();
      }
   }
},

memory:如果不存在,则为undefired,存在则为[this,args],fire()执行传入的参数

firingIndex为循环遍历起始位置,根据firingStart的值来确定,firingStart每次fire完毕后都会归0,正常调用是从0开始,但是当从add里面里面执行fire时,firingStart的值为list的长度,也就是说add执行fire方法,是从添加之前list的索引开始遍历的。

args = [ context, args.slice ? args.slice() : args ];
for循环判断,如果创建有stopOnFalse,memory=false不执行后续add的函数,跳出循环

list存在并且stack有值,再次重复fire

if ( list ) {
      if ( stack ) {
         if ( stack.length ) {
            fire( stack.shift() );
         }
      } else if ( memory ) {
         list = [];
      } else {
         self.disable();
      }
   }
只是$.Callbacks运行的主要过程,其他方法就很简单了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值