“约见”面试官系列之常见面试题第二十一篇之函数防抖和节流(建议收藏)
目錄
?
前言
概念
函數防抖(debounce)
函數節流(throttle)
常見應用場景
函數防抖的應用場景
函數節流的應用場景
實現原理
函數防抖(debounce)
函數節流(throttle)
異同比較
lodash中的 Debounce 、Throttle
如何使用 debounce
使用示例
實現
總結
前言
最近和前端的小伙伴們,在討論面試題的時候。談到了函數防抖和函數節流的應用場景和原理。于是,想深入研究一下兩者的異同。對于后端而言,函數防抖、函數節流的使用場景并不是很多。但是,對于前端使用卻是很常見。常見實用場景,有滾動加載、搜索框輸入、窗口大小拖拽 Resize。
概念
函數防抖(debounce)
函數防抖,就是指觸發事件后在 n 秒內函數只能執行一次,如果在 n 秒內又觸發了事件,則會重新計算函數執行時間。
簡單的說,當一個動作連續觸發,則只執行最后一次。
打個比方,坐公交,司機需要等最后一個人進入才能關門。每次進入一個人,司機就會多等待幾秒再關門。
函數節流(throttle)
限制一個函數在一定時間內只能執行一次。
舉個例子,乘坐地鐵,過閘機時,每個人進入后3秒后門關閉,等待下一個人進入。
為了方便理解,我們首先通過一個可視化的工具,感受一下三種環境(正常情況、函數防抖情況 debounce、函數節流 throttle)下,對于mousemove事件回調的執行情況。
三種環境下,mousemove事件執行分布圖
?
豎線的疏密代表事件執行的頻繁程度。可以看到,正常情況下,豎線非常密集,函數執行的很頻繁。而debounce(函數防抖)則很稀疏,只有當鼠標停止移動時才會執行一次。throttle(函數節流)分布的較為均已,每過一段時間就會執行一次。
常見應用場景
函數防抖的應用場景
連續的事件,只需觸發一次回調的場景有:
- 搜索框搜索輸入。只需用戶最后一次輸入完,再發送請求
- 手機號、郵箱驗證輸入檢測
- 窗口大小Resize。只需窗口調整完成后,計算窗口大小。防止重復渲染。
函數節流的應用場景
間隔一段時間執行一次回調的場景有:
- 滾動加載,加載更多或滾到底部監聽
- 谷歌搜索框,搜索聯想功能
- 高頻點擊提交,表單重復提交
實現原理
函數防抖(debounce)
函數防抖的簡單實現:
?
const _.debounce = (func, wait) => {let timer;return () => {clearTimeout(timer);timer = setTimeout(func, wait);}; };函數防抖在執行目標方法時,會等待一段時間。當又執行相同方法時,若前一個定時任務未執行完,則 clear 掉定時任務,重新定時。
函數節流(throttle)
1)函數節流的 setTimeout 版簡單實現
?
const _.throttle = (func, wait) => {let timer;return () => {if (timer) {return;}timer = setTimeout(() => {func();timer = null;}, wait);}; };函數節流的目的,是為了限制函數一段時間內只能執行一次。因此,通過使用定時任務,延時方法執行。在延時的時間內,方法若被觸發,則直接退出方法。從而,實現函數一段時間內只執行一次。
2)函數節流的時間戳版簡單實現
根據函數節流的原理,我們也可以不依賴 setTimeout實現函數節流。
?
const throttle = (func, wait) => {let last = 0;return () => {const current_time = +new Date();if (current_time - last > wait) {func.apply(this, arguments);last = +new Date();}}; };其實現原理,通過比對上一次執行時間與本次執行時間的時間差與間隔時間的大小關系,來判斷是否執行函數。若時間差大于間隔時間,則立刻執行一次函數。并更新上一次執行時間。
異同比較
相同點:
- 都可以通過使用 setTimeout 實現。
- 目的都是,降低回調執行頻率。節省計算資源。
不同點:
- 函數防抖,在一段連續操作結束后,處理回調,利用 clearTimeout 和 setTimeout 實現。函數節流,在一段連續操作中,每一段時間只執行一次,頻率較高的事件中使用來提高性能。
- 函數防抖關注一定時間連續觸發,只在最后執行一次,而函數節流側重于一段時間內只執行一次。
lodash中的 Debounce 、Throttle
最后討論一下 lodash中 debounce的使用和源碼淺析。之所以分析 debounce,是因為在lodash中,throttle 是基于 debounce 實現的。如果能理解了 debounce的實現,也就能快速掌握 throttle。
如何使用 debounce
首先,看一下 debounce 的API。需要注意的是,API中的第三個參數 options。一共有3個屬性,分別是 leading、maxWait、trailing。含義分別是在開始之前調用、最大等待時間、在延遲后調用。
leading與 trailing的區別,一個是在等待前被調用,一個是等待后被調用。我們上文中,提到的 debounce 的簡單實現,都是等待后被調用。lodash 中默認(trailing: true)的也為等待后被調用。
?
/*** 創建一個會在 `wait` 毫秒后調用 `func` 的防抖動函數。* 最后一次傳入 `func` 參數會傳給防抖動函數,隨后調用的防抖動函數返回是最后一次 func 調用的結果。* 防抖動函數提供 cancel 方法來取消延遲的函數調用 以及 flush 方法來立即執行函數調用。** 注意: 如果 leading 和 trailing 都設定為 true,則 func 允許 trailing 方式調用的條件為: 在 wait 期間多次調用。* * @param {Function} func 要防抖動的函數* @param {number} [wait=0] 需要延遲的毫秒數* @param {Object} [options={}] 選項對象* @param {boolean} [options.leading=false] 指定調用在延遲開始前* @param {number} [options.maxWait] 設置 `func` 允許被延遲的最大值* @param {boolean} [options.trailing=true] 指定調用在延遲結束后* @returns {Function} 返回一個具有防抖動功能的函數*/_.debounce(func, [wait=0], [options])使用示例
?
// 正確的用法 $(window).on('scroll', _.debounce(doSomething, 200));// 錯誤的用法 // 會導致多次調用debounce $(window).on('scroll', function() {_.debounce(doSomething, 300); });// 點擊后立即執行 sendMail $('.btn').on('click', _.debounce(sendMail, 300, {'leading': true,'trailing': false }));// `batchLog` 調用1次之后,1秒內會被觸發。 const debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });實現
推薦先粗略閱讀 lodash源碼,若難度較大,可以參考這篇博文——聊聊lodash的debounce實現,以及作者的 debounce 簡單實現。
54行實現 debounce 和 throttle,雖然功能不如 lodash 強大,但是非常適合理解 debounce的實現。
在理解 debounce 實現原理上(若不理解,可以返回閱讀上文中——函數防抖的簡單實現),主要從三個功能點理解:
- leading 功能的實現
- maxWait 功能的實現
- trailing 控制
總結
最后,總結一下函數防抖與函數節流的區別。函數防抖,將多次執行的事件合并成一次。函數節流,保持一段時間執行一次。推薦閱讀「涂鴉碼龍」翻譯的這篇 - 實例解析防抖動(Debouncing)和節流閥(Throttling),加深理解。文章豐富的實例,可深刻感受一下兩者的區別。
在不是很理解 debounce的API的情況下,直接閱讀lodash源碼,花了2個晚上看得懂云里霧里。后面,重新閱讀API文檔,弄明白了 leading 和 trailing的目的。很快就看懂了 debounce的源碼。因此,建議閱讀源碼前,先理解API中各個參數的用處。帶著目的看源碼會容易一些。
本面試題為前端??济嬖囶},后續有機會繼續完善。我是歌謠,一個沉迷于故事的講述者。
歡迎一起私信交流。
“睡服“面試官系列之各系列目錄匯總(建議學習收藏)?
總結
以上是生活随笔為你收集整理的“约见”面试官系列之常见面试题第二十一篇之函数防抖和节流(建议收藏)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 前端学习(2023)vue之电商管理系统
- 下一篇: App 上架 AppStore和证书的各