一、前言
想必前端工程師看到這兩個單詞都是十分的熟悉。這兩種技術,都是為了避免短時間內大量觸發同一種操作所做的努力。特別是,當這一操作代價比較大,如涉及到DOM操作或瀏覽器迴流等行為時,會造成極大的效能損耗,嚴重時甚至會導致瀏覽器的崩潰。
二、一些錯誤的認識
debounce(防抖)和throttle(節流),這兩種方法的原理很類似。都是利用setTimeout和閉包控制事件處理程式的觸發頻率。然而兩種方法的具體實現方法及最終效果又有細微的不同。正因為兩種方法是如此的類似,以及網上資料的混亂,加重了初學者的學習成本。
- 淺談javascript的函式節流 騰訊AlloyTeam團隊blog
- javascript高階語言程式設計(第三版)
- 函式節流與函式防抖
function debounce(fn, interval = 300) {
let timeout = null;
return function () {
clearTimeout(timeout);
timeout = setTimeout(() => {
fn.apply(this, arguments);
}, interval);
};
}
複製程式碼
對於同一段程式碼,這三處地方卻給出了完全不同的解釋:AlloyTeam團隊和javascript高階語言程式設計都將其標註為throttle,而包括第三篇blog在內的幾乎所有其他地方都將其標註為debounce。我陷入了疑惑。本著前兩者的權威性,一開始我也認為這是防抖,但是,直到我找到了這個:
其中明確指出“Debouncing enforces that a function not be called again until a certain amount of time has passed without it being called”。因此,上述程式碼應該是debounce(防抖)。 而相應的,throttle(節流)對應的描述則是“Throttling enforces a maximum number of times a function can be called over time”。
三、debounce(防抖)
廢話不多說,先上段程式碼
var debounce = function(fn, delay){
var timer = null;
return function(){
var context = this, args = arguments;
clearTimeout(timer);
timer = setTimeout(function(){
fn.apply(context, args);
}, delay);
};
};
div.onclick = debounce(() => { console.log('點選事件') }, 1000);
複製程式碼
debounce的效果是:當事件被觸發時,事件處理函式不會立即執行,而是會被放到setTimeout中延時delay執行;而當delay時間內再次觸發事件,則前一個setTimeout生成的ID會被clearTimeout清除,同時生成新的ID。這樣,只有在delay時間段內,沒有新的event,事件處理函式才會真正執行。
這一方法可以用在輸入框中,若是每次input事件觸發時,都傳送一個ajax操作,則會造成極大的效能浪費。使用debounce,則只會在使用者停止輸入後才會傳送ajax請求。
但是這一方式也有自己的問題:如果事件一直源源不斷的觸發,則可能導致在很長一段時間內,事件處理程式都不會被呼叫。在某些場景下,這可能會導致使用者體驗不佳。比如說,需要為dialog彈窗增加滑鼠拖動的功能,如果使用debounce的方法,則可能會導致在滑鼠移動期間,彈窗一直不動,直到滑鼠停止移動,彈窗會一下子跳到終止位置。這顯然是不合理的。
四、throttle(節流)
function throttle(fun, delay) {
let last, deferTimer
return function (args) {
let that = this
let _args = arguments
let now = +new Date()
if (last && now < last + delay) {
clearTimeout(deferTimer)
deferTimer = setTimeout(function () {
last = now
fun.apply(that, _args)
}, delay)
}else {
last = now
fun.apply(that,_args)
}
}
}
let throttleAjax = throttle(ajax, 1000)
let inputc = document.getElementById('throttle')
inputc.addEventListener('keyup', function(e) {
throttleAjax(e.target.value)
})
複製程式碼
throttle的的效果和debounce類似,也是會限制delay時間內事件的觸發次數。但是,相較於debounce,throttle的好處在於當事件連續觸發時,每delay時間段,事件處理函式都會執行一次。從而避免了debounce的連續觸發事件,導致事件處理函式一直無法呼叫的極端情況。
throttle可以用在滾屏預載入,彈窗拖動等場景。這些場景的特徵是,在事件節流的基礎上,要求每隔一段時間,至少執行一次事件處理函式。
五、總結
debounce解決了短時間內大量事件觸發的問題,只有當delay時間段內,沒有新的事件觸發,才會真正執行事件處理函式;但是,極端情況下,事件處理函式可能一直得不到執行。
throttle則在解決事件節流的基礎上,定期執行事件處理函式。
ps:以上純屬個人理解,如有錯誤,歡迎大家指出