jQuery.Callbacks()是在版本1.7中新加入的。它是一個多用途的回調函數清單對象,提供了一種強大的方法來管理回調函數隊列。
1、使用場景:
var callbacks = $.Callbacks();
callbacks.add(function() {
alert('a');
})
callbacks.add(function() {
alert('b');
})
callbacks.fire(); //輸出結果: 'a' 'b'
便捷的處理參數
-
: 確定這個回調清單隻執行( .fire() )一次(像一個遞延 Deferred).once
-
: 保持以前的值,将添加到這個清單的後面的最新的值立即執行調用任何回調 (像一個遞延 Deferred).memory
-
: 確定一次隻能添加一個回調(是以在清單中沒有重複的回調).unique
-
: 當一個回調傳回false 時中斷調用stopOnFalse
例如:
var callbacks = $.Callbacks('once');
callbacks.add(function() {
alert('a');
})
callbacks.add(function() {
alert('b');
})
callbacks.fire(); //輸出結果: 'a' 'b'
callbacks.fire(); //未執行
2、jQuery.Callbacks()的API:
callbacks.add() 回調清單中添加一個回調或回調的集合。
callbacks.disable() 禁用回調清單中的回調
callbacks.disabled() 确定回調清單是否已被禁用。
callbacks.empty() 從清單中删除所有的回調.
callbacks.fire() 用給定的參數調用所有的回調
callbacks.fired() 通路給定的上下文和參數清單中的所有回調。
callbacks.fireWith() 通路給定的上下文和參數清單中的所有回調。
callbacks.has() 确定清單中是否提供一個回調
callbacks.lock() 鎖定目前狀态的回調清單。
callbacks.locked() 确定回調清單是否已被鎖定。
callbacks.remove() 從回調清單中的删除一個回調或回調集合。
源碼分析:
jQuery.Callbacks = function(options) {
// Convert options from String-formatted to Object-formatted if needed
// (we check in cache first)
//通過字元串在optionsCache尋找有沒有相應緩存,如果沒有則建立一個,有則引用
//如果是對象則通過jQuery.extend深複制後賦給options。
options = typeof options === "string" ?
(optionsCache[options] || createOptions(options)) :
jQuery.extend({}, options);
var // Last fire value (for non-forgettable lists)
memory, // 最後一次觸發回調時傳的參數
// Flag to know if list was already fired
fired, // 清單中的函數是否已經回調至少一次
// Flag to know if list is currently firing
firing, // 清單中的函數是否正在回調中
// First callback to fire (used internally by add and fireWith)
firingStart, // 回調的起點
// End of the loop when firing
firingLength, // 回調時的循環結尾
// Index of currently firing callback (modified by remove if needed)
firingIndex, // 目前正在回調的函數索引
// Actual callback list
list = [], // 回調函數清單
// Stack of fire calls for repeatable lists
stack = !options.once && [], // 可重複的回調函數堆棧,用于控制觸發回調時的參數清單
// Fire callbacks// 觸發回調函數清單
fire = function(data) {
//如果參數memory為true,則記錄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) {
// 阻止未來可能由于add所産生的回調
memory = false; // To prevent further calls using add
break; //由于參數stopOnFalse為true,是以當有回調函數傳回值為false時退出循環
}
}
//标記回調結束
firing = false;
if (list) {
if (stack) {
if (stack.length) {
//從堆棧頭部取出,遞歸fire
fire(stack.shift());
}
} else if (memory) { //否則,如果有記憶
list = [];
} else { //再否則阻止回調清單中的回調
self.disable();
}
}
},
// Actual Callbacks object
// 暴露在外的Callbacks對象,對外接口
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傳進來的清單的每一個對象執行操作
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?
// 如果回調清單中的回調正在執行時,其中的一個回調函數執行了Callbacks.add操作
// 上句話可以簡稱:如果在執行Callbacks.add操作的狀态為firing時
// 那麼需要更新firingLength值
if (firing) {
firingLength = list.length;
// With memory, if we're not firing then
// we should call right away
} else if (memory) {
//如果options.memory為true,則将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循環的意義在于借助于強大的jQuery.inArray删除函數清單中相同的函數引用(沒有設定unique的情況)
// jQuery.inArray将每次傳回查找到的元素的index作為自己的第三個參數繼續進行查找,直到函數清單的盡頭
// splice删除數組元素,修改數組的結構
while ((index = jQuery.inArray(arg, list, index)) > -1) {
list.splice(index, 1);
// Handle firing indexes
// 在函數清單處于firing狀态時,最主要的就是維護firingLength和firgingIndex這兩個值
// 保證fire時函數清單中的函數能夠被正确執行(fire中的for循環需要這兩個值
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()的核心思想是 Pub/Sub 模式,建立了程式間的松散耦合和高效通信。
pub/sub (觀察者模式) 的背後,總的想法是在應用程式中增強松耦合性。并非是在其它對象的方法上的單個對象調用。一個對象作為特定任務或是另一對象的活動的觀察者,并且在這個任務或活動發生時,通知觀察者。觀察者也被叫作訂閱者(Subscriber),它指向被觀察的對象,既被觀察者(Publisher 或 subject)。當事件發生時,被觀察者(Publisher)就會通知觀察者(subscriber)。