$.Callbacks是在版本1.7中新加入的。它是一個多用途的回呼函數列表對象,提供了一種強大的方法來管理回呼函數隊列。
整個$.Callbacks的源碼不到200行,它是一個工廠函數,使用函數調用方式(非new,它不是一個類)建立對象,它有一個選擇性參數flags用來設定回呼函數的行為。
$.Callbacks是在jQuery內部使用,如為$.ajax,$.Deferred等組件提供基礎功能的函數。它也可以用在類似功能的一些組件中,如自己開發的外掛程式。
$.Callbacks構造的對象(以callbacks樣本)主要包括以下方法:
- callbacks.add
- callbacks.remove
- callbacks.has
- callbacks.empty
- callbacks.disable
- callbacks.fire
- callbacks.fireWith
- callbacks.fired
- callbacks.lock
- callbacks.locked
1. callbacks.add 添加一個函數到回調隊列
function fn1() {console.log(1)}function fn2() {console.log(2)}var callbacks = $.Callbacks();// 方式1callbacks.add(fn1);// 方式2 一次添加多個回呼函數callbacks.add(fn1, fn2);// 方式3 傳數組callbacks.add([fn1, fn2]);// 方式4 函數和數組摻和callbacks.add(fn1, [fn2]);
傳數組進去實際在add內部判斷如果是數組會遞迴調用私人的add函數。此外,需注意add方法預設不去重,比如這裡fn1添加兩次,fire時會觸發兩次。私人add方法有趣,它使用了具名函數立即執行其名僅在函數內可用。中所圈之處
2. callbacks.remove 從回調隊列中刪除一個函數
function fn1() { console.log(1)}function fn2() { console.log(2)}var callbacks = $.Callbacks();callbacks.add(fn1, fn2);callbacks.remove(fn1);
此時fire只會觸發fn2了。
此外remove方法會把添加多次的函數如fn1,全部刪除掉。為此還和cmc和nick討論為啥remove內部不用if而用while(開始我以為是jQuery寫錯了)。如下
var callbacks = $.Callbacks();callbacks.add(fn1, fn2, fn1, fn2);callbacks.remove(fn1);
此時會把add兩次的fn1都刪掉,fire時只觸發fn2兩次。換成if則只刪fn1一次。
3. callbacks.has 判斷是否添加過某回呼函數,不想重複添加時很有用
function fn1() { console.log(1)}var callbacks = $.Callbacks();callbacks.add(fn1);if (!callbacks.has(fn1)) { callbacks.add(fn1);}
4. callbacks.empty 清空所有的回呼函數
function fn1() {console.log(1)}function fn2() {console.log(2)}var callbacks = $.Callbacks();callbacks.add(fn1);callbacks.add(fn2);callbacks.empty();
此時再fire不會觸發任何函數。empty函數實現很簡單,只是把內部的隊列管理對象list重設為一個空數組。這裡可以瞭解清空數組的幾種方式。
5. callbacks.disable 禁用該對象
function fn1() { console.log(1)}function fn2() { console.log(2)}var callbacks = $.Callbacks();callbacks.disable();callbacks.add(fn1); // 不起作用callbacks.add(fn2); // 不起作用callback.remove(fn1); // 不起作用callbacks.fire(); // 不起作用
調用後再使用add, remove, fire等方法均不起作用。該方法內不是實際是將隊列管理對象list、stack、memory都置undefined了。
6. callbacks.fire 主動觸發添加回呼函數
function fn() { console.log(this); // 上下文是callbacks console.log(arguments); // [3]}var callbacks = $.Callbacks();callbacks.add(fn);callback.fire(3);
前面已經提到了,fire方法用來觸發回呼函數,預設的上下文是callbacks對象,還可以傳參給回呼函數。
7. callbacks.fireWith 同fire,但可以指定執行內容
function fn() { console.log(this); // 上下文是person console.log(arguments); // [3]}var person = {name: 'jack'};var callbacks = $.Callbacks();callbacks.add(fn);callback.fireWith(person, 3);
其實fire內部調用的是fireWith,只是將上下文指定為this了,而this正是$.Callbacks構造的對象。
8. callbacks.fired 判斷是否有主動觸發過(調用fire或fireWith方法)
function fn1() { console.log(1)}var callbacks = $.Callbacks();callbacks.add(fn1);callbacks.fired(); // falsecallbacks.fire();callbacks.fired(); // true
注意,只要調用過一次fire或fireWith就會返回true。
9. callbacks.lock 鎖定隊列的狀態
10. callbacks.locked 判斷是否鎖定狀態
$.Callbacks構造時可配置的參數Flags是可選的,字串類型以空格分隔,有如下
- once
- memory
- unique
- stopOnFalse
1. once 確保回呼函數僅執行一次
function fn() { console.log(1)}var callbacks = $.Callbacks('once');callbacks.add(fn);callbacks.fire(); // 列印1callbacks.fire(); // fn不再觸發
2. memory 記憶callbacks
function fn1() {console.log(1)}function fn2() {console.log(2)}var callbacks = $.Callbacks('memory');callbacks.add(fn1);callbacks.fire(); // 必須先firecallbacks.add(fn2); // 此時會立即觸發fn2
memory有點繞,本意是記憶的意思。實際它的用法有點詭異,需結合特定情境來看(如jQuery.Deferred)。當首次調用fire後,之後每次add都會立即觸發。比如先callbacks.fire(),再callbacks.add(fn1),這時fn1會立即被調用。
如果是大量新增的,也都會被觸發
function fn1() {console.log(1)}function fn2() {console.log(2)}function fn3() {console.log(3)}var callbacks = $.Callbacks('memory');callbacks.add(fn1);callbacks.fire();callbacks.add([fn2, fn3]); // output 2, 3
3. unique 去除重複的回呼函數
function fn1() {console.log(1)}function fn2() {console.log(2)}var callbacks = $.Callbacks('unique');callbacks.add(fn1);callbacks.add([fn1, fn2]); // 再次添加fn1callbacks.fire(); // output 1, 2
這個很好理解,之前用has判斷去重,使用unique屬性則更方便。本例先add一次fn1,第二次再add時內部則會去重。因此最後fire時只輸出“1,2”而不是“1,1,2”。
4. stopOnFalse 回呼函數返回false時中斷回調隊列的迭代
function fn1() {console.log(1)}function fn2() {console.log(2)return false // 注意這裡}function fn3() {console.log(3)}var callbacks = $.Callbacks('stopOnFalse');callbacks.add(fn1, fn2, fn3);callbacks.fire(); // output 1,2
從該屬性名稱就能知了它的意圖,即回呼函數通過return false來停止後續的回調執行。該樣本添加了3個回調,fn2中使用return false,當fire執行到fn2時會停止執行,後續的fn3就不會被調用了。
用$.Callbacks實現觀察者模式
// 觀察者模式var observer = {hash: {},subscribe: function(id, callback) {if (typeof id !== 'string') {return}if (!this.hash[id]) {this.hash[id] = $.Callbacks()this.hash[id].add(callback)} else {this.hash[id].add(callback)}},publish: function(id) {if (!this.hash[id]) {return}this.hash[id].fire(id)}}// 訂閱observer.subscribe('mailArrived', function() {alert('來信了')})observer.subscribe('mailArrived', function() {alert('又來信了')})observer.subscribe('mailSend', function() {alert('發信成功')})// 發布setTimeout(function() {observer.publish('mailArrived')}, 5000)setTimeout(function() {observer.publish('mailSend')}, 10000)
註:閱讀版本為1.8.3