1. 回撥示例
如果有個模組 findeNodes()
,任務是找到期望的 DOM 元素並使用 hide()
處理:
function findNodes() {
var i = 10000, nodes = [], found
while (i--) {
// ...複雜邏輯,篩選出符合的元素 found
nodes.push(found)
}
return nodes
}
function hide(nodes) {
for (let i = 0, max = nodes.length; i < max; i++) {
nodes[i].style.display = 'none'
}
}
hide(findNodes())
2. 改進
可以看到函式 findNodes()
和 hide()
分別兩次進行了迴圈,這是十分低效的,如果要避免這種重複迴圈,並且只要在 findNodes()
中選擇的時候就進行 hide()
那麼將是高效的實現方式。如果在 findNodes()
中實現修改邏輯,由於檢索和修改邏輯耦合,那麼它將不再是一個通用函式。對這種問題的解決方法是採用回撥模式。
可以將節點隱藏邏輯以回撥函式的方式傳遞給 findNodes()
並委託執行:
function findNodes(callback) {
var i = 10000, nodes = [], found
if (typeof callback !== 'function') { // 檢查引數是否為可呼叫
callback = false
}
while (i--) {
// ...複雜邏輯,篩選出符合的元素 found
if (callback) {
callback(found)
}
nodes.push(found)
}
return nodes
}
function hide(nodes) {
nodes[i].style.display = 'none'
}
findNodes(hide)
那麼現在回撥函式可選,重構後加入回撥函式引數的 findNodes()
仍然可以像以前一樣使用,而不會破壞舊 API 的原始程式碼。
3. 回撥與作用域
前面的例子中,回撥執行的語句:callback(para)
,在多數情況下有效,但是如果傳遞的函式是物件的方法且有 this
那麼回撥方法裡的 this
將指向的是全域性物件,從而發生意外。
解決這個問題的方法是傳遞迴調函式,並且還傳遞該回撥函式所屬的物件:
function findNodes (callback, callback_obj){
...
if (typeof callback === 'function'){
callback.call(callback_obj, found)
}
...
}
findNodes (obj.sayName, obj)
當然,可以把方法作為字串來傳遞,避免重複兩次輸入該物件的名稱:
findNodes (callback, callback_obj){
if (typeof callback === 'string'){
callback = callback_obj[callback]
}
if (typeof callback === 'function'){
callback.call(callback_obj, found)
}
}
findNodes('sayName', Obj)
本文是系列文章,可以相互參考印證,共同進步~
網上的帖子大多深淺不一,甚至有些前後矛盾,在下的文章都是學習過程中的總結,如果發現錯誤,歡迎留言指出~
參考:<JavaScript 模式> P65