防抖函式Debounce實現
阿新 • • 發佈:2021-12-21
實現原理就是利用定時器,函式第一次執行時設定一個定時器,之後呼叫時發現已經設定過定時器就清空之前的定時器,並重新設定一個新的定時器,如果存在沒有被清空的定時器,當定時器計時結束後觸發函式執行。 |
防抖函式 debounce 指的是某個函式在某段時間內,無論觸發了多少次回撥,都只執行最後一次。
// fn 是需要防抖處理的函式 // wait 是時間間隔 function debounce(fn, wait = 50) { // 通過閉包快取一個定時器 id let timer = null // 將 debounce 處理結果當作函式返回 // 觸發事件回撥時執行這個返回函式 return function(...args) { // this儲存給context const context = this // 如果已經設定過定時器就清空上一次的定時器 if (timer) clearTimeout(timer) // 開始設定一個新的定時器,定時器結束後執行傳入的函式 fn timer = setTimeout(() => { fn.apply(context, args) }, wait) } } // DEMO // 執行 debounce 函式返回新函式 const betterFn = debounce(() => console.log('fn 防抖執行了'), 1000) // 停止滑動 1 秒後執行函式 () => console.log('fn 防抖執行了') document.addEventListener('scroll', betterFn)
不過 underscore 中的 debounce 還有第三個引數:immediate 。這個引數是做什麼用的呢?
傳參 immediate 為 true, debounce會在 wait 時間間隔的開始呼叫這個函式 。(注:並且在 wait 的時間之內,不會再次呼叫。)在類似不小心點了提交按鈕兩下而提交了兩次的情況下很有用。
把 true 傳遞給 immediate 引數,會讓 debounce 在 wait 時間開始計算之前就觸發函式(也就是沒有任何延時就觸發函式),而不是過了 wait 時間才觸發函式,而且在 wait 時間內也不會觸發(相當於把 fn 的執行鎖住)。如果不小心點了兩次提交按鈕,第二次提交就會不會執行。
那我們根據 immediate 的值來決定如何執行 fn 。如果是 immediate 的情況下,我們立即執行 fn ,並在 wait 時間內鎖住 fn 的執行, wait 時間之後再觸發,才會重新執行 fn ,以此類推。
// immediate 表示第一次是否立即執行 function debounce(fn, wait = 50, immediate) { let timer = null return function(...args) { // this儲存給context const context = this if (timer) clearTimeout(timer) // immediate 為 true 表示第一次觸發後執行 // timer 為空表示首次觸發 if (immediate && !timer) { fn.apply(context, args) } timer = setTimeout(() => { fn.apply(context, args) }, wait) } } // DEMO // 執行 debounce 函式返回新函式 const betterFn = debounce(() => console.log('fn 防抖執行了'), 1000, true) // 第一次觸發 scroll 執行一次 fn,後續只有在停止滑動 1 秒後才執行函式 fn document.addEventListener('scroll', betterFn)
看完了上文的基本版程式碼,感覺還是比較輕鬆的,現在來學習下 underscore 是如何實現 debounce 函式的,學習一下優秀的思想,直接上程式碼和註釋,本原始碼解析依賴於 underscore 1.9.1 版本實現。
// 此處的三個引數上文都有解釋 _.debounce = function(func, wait, immediate) { // timeout 表示定時器 // result 表示 func 執行返回值 var timeout, result; // 定時器計時結束後 // 1、清空計時器,使之不影響下次連續事件的觸發 // 2、觸發執行 func var later = function(context, args) { timeout = null; // if (args) 判斷是為了過濾立即觸發的 // 關聯在於 _.delay 和 restArguments if (args) result = func.apply(context, args); }; // 將 debounce 處理結果當作函式返回 var debounced = restArguments(function(args) { if (timeout) clearTimeout(timeout); if (immediate) { // 第一次觸發後會設定 timeout, // 根據 timeout 是否為空可以判斷是否是首次觸發 var callNow = !timeout; timeout = setTimeout(later, wait); if (callNow) result = func.apply(this, args); } else { // 設定定時器 timeout = _.delay(later, wait, this, args); } return result; }); // 新增 手動取消 debounced.cancel = function() { clearTimeout(timeout); timeout = null; }; return debounced; }; // 根據給定的毫秒 wait 延遲執行函式 func _.delay = restArguments(function(func, wait, args) { return setTimeout(function() { return func.apply(null, args); }, wait); });
相比上文的基本版實現,underscore 多了以下幾點功能。
- 1、函式 func 的執行結束後返回結果值 result
- 2、定時器計時結束後清除 timeout ,使之不影響下次連續事件的觸發
- 3、新增了手動取消功能 cancel
- 4、immediate 為 true 後只會在第一次觸發時執行,頻繁觸發回撥結束後不會再執行
本文地址:https://www.linuxprobe.com/anti-shake-function-implementation.html