借用百度百科來講明下回調函數:ajax
回調函數就是一個經過函數指針調用的函數。若是你把函數的指針(地址)做爲參數傳遞給另外一個函數,當這個指針被用來調用其所指向的函數時,咱們就說這是回調函數。回調函數不是由該函數的實現方直接調用,而是在特定的事件或條件發生時由另外的一方調用的,用於對該事件或條件進行響應。設計模式
jQuery回調對象實現剛好利用了設計模式中的觀察者模式思想,觀察者模式 (pub/sub) 的背後,總的想法是在應用程序中加強鬆耦合性。並不是是在其它對象的方法上的單個對象調用。一個對象做爲特定任務或是另外一對象的活動的觀察者,而且在這個任務或活動發生時,通知觀察者。觀察者也被叫做訂閱者(Subscriber),它指向被觀察的對象,既被觀察者(Publisher 或 subject)。當事件發生時,被觀察者(Publisher)就會通知觀察者(subscriber)數組
具體到開發中,咱們會在事件觸發,定時器,ajax、動畫(transformend)使用回調函數。閉包
jQuery中回調函數隊列管理模塊:Callbacks從1.6版中的_Deferred對象中抽離出來的。其設計原理是開始構建一個存放回調的數組,再經過add、remove、fire、lock等操做來控制函數隊列的管理,並提供once、memory、unique、stopOnFalse四個option進行一些特殊的控制。app
代碼以下:函數
var rnotwhite = (/\S+/g);//匹配空格,返回數組 // String to Object options format cache 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( rnotwhite ) || [], function( _, flag ) {//接受的字符串的組合傳參數,可使用空格分割 object[ flag ] = true; }); return object; } /* * Create a callback list using the following parameters: * By default a callback list will act like an event callback list and can be * "fired" multiple times. * * Possible options://這裏提供四個可選參數once、memory、unique、stopOnFalse 對函數隊列進行特殊控制。 */ 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 && [],//傳遞once參數後,則回調執行完畢以後,list會被清空([])或置爲undefined // Fire callbacks fire = function( data ) {//閉包方法 memory = options.memory && data; fired = true;//用來判斷回調隊列是否被執行過一次 firingIndex = firingStart || 0;//上次fire的位置,用做設置memory時,add以後直接觸發回調。 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---//強制修改memory break; } } firing = false; if ( list ) { if ( stack ) {//是否爲false,初始化時是經過是否傳遞once來控制, if ( stack.length ) { fire( stack.shift() ); } } else if ( memory ) {//這裏清空目的在於,add以後直接fire新添加的回調。 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 ) {//添加回調list集合 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 ) {//這裏若是option.memory有值的話,memory已經被修改成[context,arguments] 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 );//若$.Callback調用時設置memory,則會閉包中memory被修改成此時的args<==>[context,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; };
一、功能上看:callbacks能夠用來在隊列中添加回調,執行回調,刪除回調等等。並提供一些參數如once,memory,unique等來進行特殊需求的控制。oop
二、回調模塊結構組織:首先構建一個存放回調的隊列,如var list = [],經過閉包使回調隊列所佔內存空間不被釋放添加回調時,將回調push進list,fire時遍歷list將回調隊列中函數執行一遍。動畫
jQuery.Callbacks()的API列表以下this
callbacks.add() 往回調隊列中添加一個回調或回調的集合。 callbacks.disable() 禁用回調列表中的回調 callbacks.disabled() 判斷回調列表是否已被禁用。 callbacks.empty() 從列表中刪除全部的回調. callbacks.fire() 用給定的參數執行全部的回調 callbacks.fired() 判斷是否回調隊列是否被至少fire一次。 callbacks.fireWith() 訪問給定的上下文和參數列表中的執行全部回調。 callbacks.has() 肯定列表中是否包含一個回調 callbacks.lock() 鎖定當前狀態的回調列表。 callbacks.locked() 肯定回調列表是否已被鎖定。 callbacks.remove() 從回調列表中的刪除一個回調或回調集合。
代碼定義了多個局部變量,來看看他們用處:spa
list:保存回掉函數隊列 經過閉包使回調數組所佔內存空間不被釋放,添加回調時,將回調push進list,執行時則遍歷list執行回調
firing:判斷是否正在執行回調隊列。
firingStart:記住當前fire的位置,當參數爲memory時使用到。
stack:若是當前正在執行回調(或者說回掉隊列未執行完),即firing爲true,將要fire的回調保存到棧中,若未設置了once爲true ,待回調隊列執行完成以後就會執行該回掉。若設置了once 則確保這個回調列表只執行( .fire() )一次。
memory :若設置了memory,保持之前的值,後面添加到這個回調隊列會當即執行該回調(像一個遞延 Deferred)
firingStart:用於add 回調以後做爲當即執行該回調的索引(在設置memory以後使用)
重點提下fire方法: self.fire –> self.fireWith –> fire 最終執行代碼是內部私有的fire方法,內部幾個邏輯處理了如下幾種狀況
(1)、每fire 一次都遍歷list回調隊列,直到結束或者有一個回調函數返回false (只有設置了stopOnFalse控制邏輯才起做用),才停止執行回調
(2)、接下來處理了,回調隊列正在執行時(firing爲true),add 了一個回調,並不會當即處理而是push 到stack以後。 待回調隊列執行完成後再根據stack判斷是否fire新增的回調。
disable方法:list = stack = memory = undefined; 後續全部操做都失效
三、參數的使用方式:參數用 一個用空格標記分隔的標誌可選列表,用來改變回調列表中的行爲:經過如下這些參數進行特殊需求的控制
once
: 確保這個回調列表只執行( .fire() )一次.memory
: 保持之前的值,當執行add以後,將運行添加到這個列表的後面的最新的回調,參數保存在memory中(像一個遞延 Deferred).unique
: 確保一次只能添加一個回調(因此在列表中沒有重複的回調).stopOnFalse
: 當一個回調返回false 時中斷調用當新建一個Callbacks回調隊列時,能夠經過這些參數定義回調隊列fire 以後的行爲。
默認狀況下,回調列表(不傳遞參數狀況下)能夠被屢次觸發(fire),下面來看看參數具體如何使用及其在源碼中如何發揮做用的。
(1)、var callbacks = $.Callbacks( "once" ); —— fire一次以後list被清空
(2)、
var callbacks = $.Callbacks( "memory" );—— memory的實現思路就是回調隊列list在fire以後(無論是否設置once),
之後add的時候自動調用fire,將新添加的回調執行一遍。(相似遞延)
(3)、
var callbacks = $.Callbacks( "
unique
" );——控制回調函數的惟一性。經過has方法判斷是否存在隊列中。
(4)、
var callbacks = $.Callbacks( "stopOnFalse" );——控制是否須要回調函數返回值是否爲false來終止回調隊列的執行。
(5)、
var callbacks = $.Callbacks( "once memory" );—— 保持之前的值,將添加到這個列表的後面的最新的值當即執行調用任何回調,once的時候只容許add一次,在觸發fire以後就會理清掉list。