jQuery源码之Callbacks详解

一.前言 

首先声明,这是本人个人笔记。不喜勿碰。

Callbacks在jQuery框架中是基础建设的中重之重,很多高级模块都是基于它实现的,事件模块,ajax模块。

在jQuery中回调对象模块其实就是一个队列,进行出列,插入队列的操作。只不过加了很多技巧,先贴出源码,其中有些调试笔记,可以参考下。

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 // Flag to know if list is currently firing
		firing,
		// Last fire value (for non-forgettable lists)
		memory,
		// Flag to know if list was already fired
		fired,
		// End of the loop when firing
		firingLength,
		// Index of currently firing callback (modified by remove if needed)
		firingIndex,
		// First callback to fire (used internally by add and fireWith)
		firingStart,
		// 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;
};
jQuery.Callbacks(arg) arg可以是once,memory,unique,这个最后返回的是一个装有API的对象,封装了其自身的属性和几个方法。只能通过开放的API进行调用

传入的传参数不一样,他们的回调函数也会有不用一样的功能。

1.once

function a(){
		console.log('a');
		
	}
	function b(){
		console.log('b');
	}
	var callbacks = $.Callbacks('once');

	callbacks.add( a );
	
	callbacks.fire();
	callbacks.add( b );
	
	callbacks.fire();
输出: a

只激发了第一次添加的a函数,后面新增的然后在激发也没有执行.

只要激发一次以后,就不能在激发这个回调函数队列,因为在第一次激发后 self.disable();,禁用了该队列,尽管在ADD函数进去,也不能激发。

因为disable让list = stack = memory = undefined;

2.memory

var callbacks = $.Callbacks('memory');

	callbacks.add( a );
	
	callbacks.fire();
	callbacks.add( b );
输出: a

          b

先新增a函数,在激发,然后在新增b函数,按道理讲第二次新增b函数的时候没有激发,不会执行b函数,但是这就是memory的作用,按我的理解就是记忆,新增就是执行。

只不过在第一次激发之前没有记忆功能,但是在激发一次过后就有记忆的能力,你下次在新增函数的时候就直接运行.

只要激发一次后,激活memory,然后每次add函数的时候,就会立即执行该函数.  随时都可以激发该回调队列

3.unique

var callbacks = $.Callbacks('unique');

	callbacks.add( a );
	callbacks.add( a );
	callbacks.fire();
输出: a

为什么只执行了一次a函数,不是add了两次吗?这就是unique的作用,相同的函数在函数队列list里面只能被添加一次

if ( !options.unique || !self.has( arg ) ) {	//	不唯一||不存在
									list.push( arg );									
								}

在add函数到队列的时候,检查是否已经添加过,如果添加过,则无视,否则就添加家进去,随时都可以激发该回调队列


二.jQuery应用回调模块

jQuery中事件和AJAX模块都用了这个回调模块,但是大部分都是jQuery.Callbacks('once memory')来构造

once和memory合起来,能让这个回调队列有什么功能呢?

1.能多次新增函数,多次激发

2.不过每次激发完后,之前添加进去的函数在list里面都会被清空 list = [] 。  而once方式是让list = undefined.

3.就是新增进去的函数只能执行一次,执行完被清空。

三,总结

jQuery运用了很对技巧,只是一个简单的队列,却会如此神奇。

猜你喜欢

转载自blog.csdn.net/luolaifa000/article/details/44597265
今日推荐