再也不怕面試官問我防抖與節流了
最近去面試,又遇到面試官問我防抖與節流了,而明明前幾天就看過手寫代碼,卻寫不出來。有時候我在想,是不是自己太笨了。
回歸正題
防抖
先不說概念,按自己的理解,在單反里,有防抖機制。因為人在拿著單反的時候會手抖(單反重),按下快門的瞬間,照片會糊,所以有防抖機制,以防止新手把照片拍糊。
單反中的防抖是防止抖動,讓人拍出清晰的照片,JavaScript 中的防抖是為了什么?
同理,它的作用也是防止抖動。試想當你頻繁觸發一個事件時,就會引起不必要的性能損失,那么讓該事件在停止觸發后再觸發,以此減少部分性能。
防抖的定義
防抖就是要延遲執行,你一直操作觸發事件一直不執行,當你停止操作等待多少秒后才執行。
也就是說不管事件觸發頻率有多高,一定在事件觸發 n 秒后執行。如果在事件觸發的 n 秒又觸發了這個事件,那就以新事件的事件為準,n 秒后才執行。總之,要等你觸發完事件 n 秒內不再觸發事件,它才執行。
手寫防抖
根據定義,我們知道要在時間 n 秒后執行,那么我們就用定時器來實現:
function debounce(event, wait) {
let timer = null;
return function (...args) {
clearTimeout(timer); // 清除setTimeout,使其回調函數不執行
timer = setTimeout(() => {
event.apply(this, args)
}, wait)
}
}
代碼很簡單,即當還在觸發事件時,就清除 timer,使其在 n 秒后執行,但此寫法首次不會立即執行,為其健壯性,需加上判斷是否第一次執行的第三個參數 flag,判斷其是否立即執行。
function debounce(event, wait, flag) {
let timer = null;
return function (...args) {
clearTimeout(timer)
if (!timer && flag) {
event.apply(this, args)
} else {
timer = setTimeout(() => {
event.apply(this, args)
}, wait)
}
}
}
防抖場景
窗口大小變化,調整樣式
window.addEventListener('resize', debounce(handleResize, 200))
搜索框,輸入后1000毫秒搜索
debounce(fetchSelectData, 300)
表單驗證,輸入 1000 毫秒后驗證
debounce(validator, 1000)
防抖帝王庫
兩大工具庫都有防抖源碼,可供參考:
- lodash-debounce。
- underscore-debounce。
節流
顧名思義,一節一節的流,就好似控制水閥,在事件不斷觸發的過程中,固定時間內執行一次事件。
手寫節流
因為是固定時間內執行一次時間,所以我們有兩種實現方法,一用時間戳,二用定時器。
時間戳
function throttle(event, wait) {
let pre = 0;
return function (...args){
if (new Date() - pre > wait) {
// 當 n 秒內不重復執行
pre = new Date();
event.apply(this, args)
}
}
}
使用時間戳雖然能實現節流,但是最后一次事件不會執行。
定時器
function throttle(event, wait) {
let timer = null;
return function (...args) {
if (!timer) {
timer = setTimeout(() => {
timer = null;
event.apply(this, args)
}, wait)
}
}
}
使用定時器實現節流,雖然最后一次能觸發,但是第一次不會觸發。
時間戳 + 定時器。
為解決第一次和最后一次都可以觸發,把兩者結合起來。
function throttle(event, wait) {
let pre = 0, timer = null;
return function (...args) {
if (new Date() - pre > wait) {
clearTimeout(timer);
timer = null;
pre = new Date();
event.apply(this, args)
} else {
timer = setTimeout(() => {
event.apply(this, args)
}, wait)
}
}
}
節流場景
scroll 滾動
window.addEventListener('scroll', throttle(handleScroll, 200))
input 動態搜索
throttle(fetchInput, 300)
節流帝王庫
- lodash-throttle。
- underscore-throttle。
總結
防抖:只執行最后一次。事件持續觸發,但只有等事件停止觸發后 n 秒后才執行函數。
節流:控制執行頻率。持續觸發,每 n 秒執行一次函數。
對比圖: