Debounce (去顫)和 Throttle (節流) 都是用在網頁中用來防止重複點擊造成重複觸發的。
假設延遲為 1 秒:
Debounce 是點擊後等待 1 秒才觸發,如果 0.5 秒時再被點擊,則重置延遲時間,1.5 秒時才會觸發。
Throttle 則是點擊後馬上觸發,在 1 秒的延遲內不論再被點擊幾次都不會再被觸發。
一般按鈕點擊的基本程式碼:
為什麼要在 debounce 中再回傳一個匿名 function? 如果不這樣做,在按鈕綁定 debounce(clickHandler)) 事件時就會直接觸發一次。
使用 setTimeout 加入延遲效果:
上面是點 10 次後每次點擊雖然都是等到延遲結束後才會觸發,但是都會觸發。現在要將程式碼改為延遲結束前如果再次觸發就重置觸發時間:
現在基本功能就做完了,每次點擊會等到延遲結束後才會觸發,且延遲結束前如果有新的點擊事件會重置延遲時間,在此前間不會被處發。
但是筆者在使用 Vue 時發現一個小問題,以前將 clickHandler 直接綁定到 button 時 this 是指向 button ,而在使用了我們自訂的 debounce 後 clickHandler 內的 this 卻指向了 window 。
要解決這個問題其實也很簡單,匿名函式就不能使用箭頭函式了,然後先將 this 儲存後在匿名函數內呼叫事件時使用 .call() 改變函數內部的 this 就可以了。
Throttle 我們只要改為執行時開啟計時器,只要發現計時器存在就不執行,然後執行時將計時器清空即可
假設延遲為 1 秒:
Debounce 是點擊後等待 1 秒才觸發,如果 0.5 秒時再被點擊,則重置延遲時間,1.5 秒時才會觸發。
Throttle 則是點擊後馬上觸發,在 1 秒的延遲內不論再被點擊幾次都不會再被觸發。
一般按鈕點擊的基本程式碼:
<button id="button">Click</button>
<script>
const button = document.getElementById('button');
function clickHandler() {
console.log('clickHandler');
}
button.addEventListener('click', clickHandler);
</script>
Debounce (去顫)程式碼說明
首先第一步先建立 debounce 函式,在裡面使用匿名函式將 fun 包裹再傳出。為什麼要在 debounce 中再回傳一個匿名 function? 如果不這樣做,在按鈕綁定 debounce(clickHandler)) 事件時就會直接觸發一次。
function clickHandler() {
console.log('clickHandler');
}
function debounce(fun) {
return () => {
fun();
};
}
button.addEventListener('click', debounce(clickHandler));
使用 setTimeout 加入延遲效果:
function debounce(fun,delay) {
return () => {
setTimeout(() => {
fun();
}, delay);
};
}
button.addEventListener('click', debounce(clickHandler, 1000));
上面是點 10 次後每次點擊雖然都是等到延遲結束後才會觸發,但是都會觸發。現在要將程式碼改為延遲結束前如果再次觸發就重置觸發時間:
function debounce(fun, delay) {
let timer;
return () => {
clearTimeout(timer);
timer = setTimeout(() => {
fun();
}, delay);
};
}
button.addEventListener('click', debounce(clickHandler, 500));
現在基本功能就做完了,每次點擊會等到延遲結束後才會觸發,且延遲結束前如果有新的點擊事件會重置延遲時間,在此前間不會被處發。
但是筆者在使用 Vue 時發現一個小問題,以前將 clickHandler 直接綁定到 button 時 this 是指向 button ,而在使用了我們自訂的 debounce 後 clickHandler 內的 this 卻指向了 window 。
要解決這個問題其實也很簡單,匿名函式就不能使用箭頭函式了,然後先將 this 儲存後在匿名函數內呼叫事件時使用 .call() 改變函數內部的 this 就可以了。
function debounce(fun, delay) {
let timer;
return function () {
const context = this;
clearTimeout(timer);
timer = setTimeout(() => {
fun.call(context);
}, delay);
};
}
button.addEventListener('click', debounce(clickHandler, 500));
debounce 完整程式碼
<button id="button">Click</button>
<script>
const button = document.getElementById('button');
function clickHandler(arg) {
console.log(this);
console.log('clickHandler', arg);
}
function debounce(fun, delay) {
let timer;
return function () {
const context = this;
clearTimeout(timer);
timer = setTimeout(() => {
fun.call(context);
}, delay);
};
}
button.addEventListener('click', debounce(()=>clickHandler(12443), 500));
</script>
Throttle (節流)程式碼說明
剛剛一步一步示範完 Debounce (去顫) 之後,要了解 Throttle (節流) 就非常簡單了,因為只是先後順序的問題。Throttle 我們只要改為執行時開啟計時器,只要發現計時器存在就不執行,然後執行時將計時器清空即可
<button id="button">Click</button>
<script>
const button = document.getElementById('button');
function clickHandler(arg) {
console.log(this);
console.log('clickHandler', arg);
}
function throttle(fun, delay) {
let timer;
return function () {
const context = this;
if (timer) return;
timer = setTimeout(() => {
fun.call(context);
timer = null;
}, delay);
};
}
button.addEventListener('click', throttle(() => clickHandler(12443), 500));
</script>
留言
張貼留言
如果有任何問題、建議、想說的話或文章題目推薦,都歡迎留言或來信: a@ruyut.com