1.定義
回調這個詞對每個js使用者是如此的熟悉不過. 從字面看很容易理解,Callback就是對調,Callbacks就是多個回掉.在JQuery中就是回調隊列, 也就是Callbacks中保存着很多個回調函數隊列, 也就是按照順序執行(按照加入隊列的順序觸發這些函數,並不意味着第一個運行結束才調用第二個回調,因爲js中存在異步)隊列中的每一個回調函數. 我們也可以這麼理解,也就是我們常說的事件訂閱和發佈模式. 說道事件,我們就容易理解了.每一次trigger就對調用一個回調函數.
那麼Callbacks有什麼用呢?在JQuery中,Deffered中使用Callbacks實現了Promise/A+標準. 解決異步的同步問題,解決代碼深層嵌套.
2.Callbacks中的主要方法
callbacks.add() 回調列表中添加一個回調或回調的集合。
callbacks.disable() 禁用回調列表中的回調
callbacks.disabled() 確定回調列表是否已被禁用。
callbacks.empty() 從列表中刪除所有的回調.
callbacks.fire() 用給定的參數調用所有的回調
callbacks.fired() 訪問給定的上下文和參數列表中的所有回調。
callbacks.fireWith() 訪問給定的上下文和參數列表中的所有回調。
callbacks.has() 確定列表中是否提供一個回調
callbacks.lock() 鎖定當前狀態的回調列表。
callbacks.locked() 確定回調列表是否已被鎖定。
callbacks.remove() 從回調列表中的刪除一個回調或回調集合。
3.主要參數
options = {
once: 回調對象僅觸發(fire)一次
memory: 若true,在Callbacks正則觸發事件過程中,新加入回調函數,新加入的函數將在舊的回調函數隊列執行完成之後,促發新加入的.
unique: 在add操作中,相同的函數僅只一次被添加(push)到回調列表中
stopOnFalse:當回調函數返回false,中斷列表中的回調循環調用,且memory === false,阻止在add操作中將要觸發的回調
}
4.源碼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
//觸發隊列, 多次觸發的隊列才把觸發的上下文入棧
//(同一個時間只有一次觸發隊列執行,也就是說連續觸發fire兩次隊列,第一次執行完,纔會執行第二次[第一次觸發,第二次觸發...])
stack = !options.once && [],
// Fire callbacks
//觸發回調函數, self使用函數fire實現最終的回調函數的調用
fire = function( data ) {
//如果是memory類型管理器, 記住fire的事件data,以便下次add的時候可以重新fire這個事件
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
//var cb = $.Callbacks();返回的實際是self對象, 在JQuery中很多這樣的使用.使用閉包實現開閉和封裝.
//有點類似Java中的內部類,例如使用內部類Iterator 實現對容器的遍歷.通過類提供的接口,限制訪問實際外部類的東西.
//self作爲Callbacks的對外接口,我們使用它來訪問Callbacks
self = {
// Add a callback or a collection of callbacks to the list
add: function() {
if ( list ) {
// First, we save the current length
//記錄本次觸發的隊列長度位置,當是memory的時候可以接着觸發新加入的回調函數
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 || [];
//棧中存的上下文格式是[上下文(默認self爲上下文), [參數1, 參數2...]]
args = [ context, args.slice ? args.slice() : args ];
//觸發回調隊列的時候,如果上一次觸發還正在進行,那麼久把當前觸發的上下文和參數保存到棧stack中,
//這樣, 上一次觸發回調完成之後,可以從棧中取出上下文和參數,接着觸發下一次
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;
}
};
//返回self
return self;
};