詳解防抖函數(debounce)和節流函數(throttle)


本文轉自:https://www.jianshu.com/p/f9f6b637fd6c

閉包的典型應用就是函數防抖和節流,本文詳細介紹函數防抖和節流的應用場景和實現。

函數防抖(debounce)

函數防抖,就是指觸發事件后,在 n 秒后只能執行一次,如果在 n 秒內又觸發了事件,則會重新計算函數的執行時間。

簡單的說,當一個動作連續觸發,只執行最后一次。

打個比方,坐公交,司機需要等最后一個人進入才能關門。每次進入一個人,司機就會多等待幾秒再關門。

函數節流(throttle)

限制一個函數在一定時間內只能執行一次

舉個例子,乘坐地鐵,過閘機時,每個人進入后3秒后門關閉,等待下一個人進入。

為了方便理解,我們首先通過一個可視化的工具,感受一下三種環境(正常情況、函數防抖情況 debounce、函數節流 throttle)下,對於mousemove事件回調的執行情況。

豎線的疏密代表事件執行的頻繁程度。可以看到,正常情況下,豎線非常密集,函數執行很頻繁。而 debounce (函數防抖)則很稀疏,只有當鼠標停止移動時,才會執行一次。throttle(函數節流)分布的較為均勻,每過一段時間就會執行一次。

常見的應用場景

函數防抖(debounce)的應用場景

連續的事件,只需觸發一次的回調場景有:

  • 搜索框搜索輸入。只需要用戶最后一次輸入完再發送請求
  • 手機號、郵箱格式的輸入驗證檢測
  • 窗口大小的 resize 。只需窗口調整完成后,計算窗口的大小,防止重復渲染。

函數節流(throttle)的應用場景

間隔一段時間執行一次回調的場景有:

  • 滾動加載,加載更多或滾動到底部監聽
  • 谷歌搜索框,搜索聯想功能
  • 高頻點擊提交,表單重復提交
  • 省市信息對應字母快速選擇

實現原理

函數防抖(debounce)

簡單實現:

const debounce = (func, wait) => {
    let timer
    return () => {
		clearTimeout(timer)
        timer = setTimeout(func, wait);
    }
}

函數防抖在執行目標方法時,會等待一段時間。當又執行相同方法時,若前一個定時任務未執行完,則 清除掉定時任務,重新定時。

封裝:

function debounce(fn, delay = 500) {
    // timer 是在閉包中的
    let timer = null;
    
    return function() {
        if (timer) {
            clearTimeout(timer)
        }
        timer = setTimeout(() => {
            fn.apply(this, arguments)
            timer = null
        }, delay)
    }
}

// test debounce 返回一個函數
input1 = document.getElementById('input1')
input1.addEventListener('keyup', debounce(function () {
    console.log(input1.value)
}, 600))

綁定事件解釋:addEventListener 第一個參數是監聽的事件,第二個參數是對應事件的回調函數。將 debounce 函數作為回調函數,這個 debounce 回調函數返回一個防抖之后的函數,因此實現了防抖的功能。

防抖解釋:當 按下某個鍵的時候觸發 keydown 事件,並執行回調。timer 默認為 null,在 return 的函數中定時器 timer 被賦值,如果在 delay 延遲之內再次觸發了 keydown 事件,那么 timer 就會被重置為null...,當用戶輸入完成之后(delay 時間已過),那么就會觸發 debounce 中的回調函數,也就是 keydown 最終要執行的事件。

函數節流(throttle)

簡單實現

const throttle = (func, wait) => {
    let timer;
    
    return () => {
        if (timer) {
            return
        }
        timer = setTimeout(() => {
            func();
            timer = null
        }, wait)
    }
}

函數節流的目的,是為了限制函數一段時間內只能執行一次。因此,通過使用定時任務,延時方法執行。在延時的時間內,方法若被觸發,則直接退出方法。從而實現一段時間內只執行一次。

封裝:

function throttle(fn, delay) {
    let timer = null
    
    return function() {
        if (timer) {
            return
        }
        timer = setTimeout(() => {
            fn.apply(this, arguments)
            timer = null
        })
    }
}
// test
let div1 = document.getElementById('div1')
div1.addEventListener('drag', throttle(function(e) {
    console.log(e.offsetX, e.offsetY)
}, 100))

解釋:如果 timer 存在,那就直接返回,不再往下執行了。這樣就實現了一段時間內執行一次的目的。

異同比較

相同點:

  • 都可以通過使用 setTimeout 實現
  • 目的都是,降低回調函數的執行頻率,節省計算資源

不同點:

  • 函數防抖,是在一段連續操作結束之后,處理回調,利用 clearTimout 和 setTimeout 實現。函數節流,是在一段連續操作中,每一段時間只執行一次,在頻率較高的事件中使用來提高性能。
  • 函數防抖關注一段時間內連續觸發,只在最后一次執行;而函數節流側重於在一段時間內只執行一次。

參考資料


免責聲明!

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



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