讀jQuery之十九(多用途回調函數列表對象)


$.Callbacks 是在版本 1.7 中新加入的。它是一個多用途的回調函數列表對象,提供了一種強大的方法來管理回調函數隊列。

整個 $.Callbacks 的源碼不到 200 行,它是一個工廠函數,使用函數調用方式(非new,它不是一個類)創建對象,它有一個可選參數flags用來設置回調函數的行為。

$.Callbacks 是在 jQuery 內部使用,如為 $.ajax,$.Deferred 等組件提供基礎功能的函數。它也可以用在類似功能的一些組件中,如自己開發的插件。

 

$.Callbacks構造的對象(以callbacks示例)主要包括以下方法:

  1. callbacks.add
  2. callbacks.remove
  3. callbacks.has
  4. callbacks.empty
  5. callbacks.disable
  6. callbacks.fire
  7. callbacks.fireWith
  8. callbacks.fired
  9. callbacks.lock
  10. callbacks.locked

 

1. callbacks.add 添加一個函數到回調隊列

function fn1() {
	console.log(1)
}
function fn2() {
	console.log(2)
}

var callbacks = $.Callbacks();
// 方式1
callbacks.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,全部刪除掉。為此還和cmcnick討論為啥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(); // false
callbacks.fire();
callbacks.fired(); // true

注意,只要調用過一次fire或fireWith就會返回true。

 

9. callbacks.lock 鎖定隊列的狀態

 

10. callbacks.locked 判斷是否鎖定狀態

 

 

$.Callbacks構造時可配置的參數Flags是可選的,字符串類型以空格分隔,有如下

  1. once
  2. memory
  3. unique
  4. stopOnFalse

 

1. once 確保回調函數僅執行一次

function fn() {
    console.log(1)
}
var callbacks = $.Callbacks('once');
callbacks.add(fn);
callbacks.fire(); // 打印1
callbacks.fire(); // fn不再觸發

 

2. memory 記憶callbacks

function fn1() {
	console.log(1)
}
function fn2() {
	console.log(2)
}
var callbacks = $.Callbacks('memory');
callbacks.add(fn1);
callbacks.fire(); // 必須先fire
callbacks.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]); // 再次添加fn1
callbacks.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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM