【每日一面】手寫防抖函數
基礎問答
問:手寫一個防抖函數
答:
/**
* 基礎版防抖函數(非立即執行)
* @param {Function} func - 需要防抖的目標函數
* @param {number} delay - 等待時間(毫秒)
* @returns {Function} - 防抖后的函數
*/
function debounce(func, delay) {
let timer = null; // 用閉包存儲定時器ID,確保多次調用共享同一個定時器
// 返回防抖后的函數,接收目標函數的參數(...args)
return function (...args) {
// 1. 若已有定時器,先清除(重復觸發時重新計時)
if (timer) clearTimeout(timer);
// 2. 重新設置定時器,等待delay后執行目標函數
timer = setTimeout(() => {
func.apply(this, args); // 用apply綁定this(確保目標函數this指向正確)
timer = null; // 執行后清空定時器,避免內存泄漏
}, delay);
};
}
擴展延伸
防抖(Debounce):
- 核心邏輯:當一個事件會被頻繁的觸發時,防抖函數不會頻繁的執行,而是等待事件停止觸發一段時間后才執行;如果在等待執行的過程中,事件再次被觸發,則我們需要重新計算需要等待的時間。
- 典型使用場景:搜索框輸入聯想(每次按鍵都會觸發輸入對應 Input 的事件,我們認為用戶結束輸入后,我們再進行聯想,這個結束輸入的判定規則就是,自上一次Input事件觸發后的一段時間內,用戶沒有再觸發Input事件,可視為結束輸入,需要開始聯想),實時輸入校驗等。
和防抖并列提及的是節流。
節流(Throttle):
- 核心邏輯:當事件會被頻繁觸發的時候,節流函數只會按照固定的時間間隔執行,無論期間事件被處罰多少次,都只在每個時間的開頭(或結束)執行一次。
- 典型使用場景:滾動監聽(一般用于加載數據判斷,每隔一段時間判斷一次當前位置,來判斷需要加載的數據量),窗口resize事件(用于重新計算布局)
這里簡單對比一下防抖和節流:
| 防抖 | 節流 | |
|---|---|---|
| 執行時機 | 事件停止觸發后,等待一段指定的時間 | 固定時間間隔執行,每個時間段內僅執行一次 |
| 重復觸發的問題 | 重新計算等待的時間,執行會延遲 | 不影響,固定時間間隔執行 |
| 目標 | 解決冗余的執行 | 解決過度的執行 |
| 使用場景 | 搜索、校驗 | 滾動加載 |
搜索輸入的過程中,每次鍵入字符觸發搜索,在沒有防抖的情況下,僅僅只有最后一次的搜索(即用戶輸入完成)才是有效的,之前的這些,全部都是沒有意義的,只會加重服務負擔,即為 “冗余” 。
頁面滾動過程中,滾動是持續觸發的,在沒有節流的情況下,每一次滾動都會有大量的計算過程(假設你的滾動事件是有計算操作的),計算阻塞主線程,會導致頁面卡頓,無法正常滾動,即為 “過度” ,如果使用防抖,滾動事件的持續觸發,會導致計算一直無法開始,俗稱“不跟手”。
面試追問
-
setTimeout 的延時并不準,有沒有辦法實現一個更精確的時間檢測?
有,使用時間戳 +requestFrameAnimation實現。 -
頁面滾動加載數據一般用什么?搜索框輸入觸發聯想詞,又用什么?
滾動加載一般用節流,防抖需要等用戶停止滾動才加載,可能會等很久,節流則是一到底部就加載,可以保證加載的及時性。
搜索聯想一般用防抖,因為用戶的輸入過程會頻繁觸發聯想,但是只有用戶停止輸入時,觸發的聯想才是用戶想要的、有效的。 -
我看你在防抖函數中,用了 apply 這是為啥?為啥不可以直接用 func ?
主要是 this 指針的指向問題,防抖函數返回的是一個新的函數,假設現在設置的是 input.oninput = debounceSearch,這個 debounceSearch 中如果有 this,那么預期是要指向 input 標簽,但是我們直接調用 func 的話,this 會指向 window 或 undefined,和預期不一致。 -
防抖函數中,如果目標函數有返回值,我們可以拿到嗎?
不行,即使返回目標函數結果也不行,因為他在 setTimeout 里面執行的,無法返回對應的執行結果。 -
但是我就需要這個返回結果,有沒有辦法?
有,兩種辦法,一是將 setTimout 用 Promise 封裝起來,setTimeout 的回調執行時,resolve 這個 Promise 就可以了,這樣防抖函數就變成了一個異步的api,二是使用回調參數,在目標函數執行后,調用這個回調就可以了。 -
有沒有遇到過防抖函數導致內存泄漏的情況?
沒有,但是防抖函數有內存泄漏的可能性,本質上是閉包寫法產生的,編寫代碼的時候注意閉包的處理就可以了。


浙公網安備 33010602011771號