JS 回撥模式

SHERlocked93發表於2017-12-14

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)

本文是系列文章,可以相互參考印證,共同進步~

  1. JS 抽象工廠模式
  2. JS 工廠模式
  3. JS 建造者模式
  4. JS 原型模式
  5. JS 單例模式
  6. JS 回撥模式
  7. JS 外觀模式
  8. JS 介面卡模式
  9. JS 利用高階函式實現函式快取(備忘模式)
  10. JS 狀態模式
  11. JS 橋接模式
  12. JS 觀察者模式

網上的帖子大多深淺不一,甚至有些前後矛盾,在下的文章都是學習過程中的總結,如果發現錯誤,歡迎留言指出~

參考:<JavaScript 模式> P65

相關文章