jQuery1.11源碼分析(10)-----Callbacks模塊

Callbacks模塊實質上就是一個回調函數隊列(固然吹得很牛逼。。好比「提供了一種強大的方法來管理回調函數隊列」),之因此介紹它是由於後面的Derferred模塊基於它。javascript

Callbacks生成時接收四個設置:once(只觸發一遍),memory(記錄前一次的觸發傳入參數,disable時是否清空隊列),unique(確保隊列中一樣的函數只有一個),stopOnFalse(當調用某一個回調函數返回false時則中止觸發)java

例:jQuery.Callbacks('once memory')jquery

Callbacks模塊還有幾個API,add,remove,has,empty,disable,lock,fire,fireWith(根據指定的上下文觸發),以及兩個狀態判斷函數,locked和disabled。api

這幾個API的用法就像名字所述的那樣,增長啊,移除啊之類的。。更多信息能夠看文檔數組

這個模塊沒什麼難度,在編寫時主要有兩個點須要考慮:1,當我添加或者刪除某個函數時,該隊列正在觸發怎麼辦?2,當我要觸發某一個隊列時,該隊列正在觸發怎麼辦?閉包

答:記錄狀態,作處理便可app

//由於隊列可能在調用時被改變,因此須要考慮兩種狀態,沒調用和調用時。函數

jQuery.Callbacks = function( options ) {

	// Convert options from String-formatted to Object-formatted if needed
	// (we check in cache first)
    //這樣處理一下之後調用就能夠保證options存在,不會像我option&&option.xxx
	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存的不是fn,而是傳給fn調用的arguments
        //這裏名稱上是stack,其實是queue
		stack = !options.once && [],
		// Fire callbacks
        //data[0]是上下文
        //data[1]是傳給各個回調函數的參數
		fire = function( data ) {
			memory = options.memory && data;
			fired = true;
			firingIndex = firingStart || 0;
			firingStart = 0;
			firingLength = list.length;
			firing = true;
			for ( ; list && firingIndex < firingLength; firingIndex++ ) {
                //當傳入的data[0]爲數組時,函數調用時的this依然是這個數組,而非數組中的item
				if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
					memory = false; // To prevent further calls using add
					break;
				}
			}
			firing = false;
            //這裏主要考慮在執行的過程當中會再次執行,因此用stack來保存傳入的參數
			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 ) {
                            console.log('list');
                            console.log(list);
							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
                                console.log('遞歸');
								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
                    //若是有memory,但咱們又不是正在觸發,因此要馬上觸發後面增長的函數
                    //這裏能夠思考一下
					} 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;
};
相關文章
相關標籤/搜索