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