01、防抖還是節流
防抖 與 節流 目的都是避免一定時間內,大量重複的操作造成的效能損耗。因此原理也類似,都是阻止過多的事件執行,只保留一部分來執行。適用場景略有不同,也有交叉,動手練習一遍就懂了。
區別 | 防抖(Debounce) | 節流(throttle) |
---|---|---|
描述 | 一定延遲時間內,連續事件只執行最後一次 | 一段固定時間內只執行一次 |
原理 | 只保留一個延時setTimeout() 的執行器,後續新的替代舊的 |
判斷時間間隔,在固定間隔時間內,只執行一次。 |
執行次數 | 只執行最後一次 | 執行首次、最後一次,或首次+最後一次 |
合適場景 | 連續操作只需要一次的,如變更內容提交到後端 | 連續操作定期執行的場景:連續的UI互動,如拖拽、滾動 |
02、什麼是防抖(Debounce)?
按字面意思理解就是 防止抖動(Debounce /di'bauns/ 防抖動),本來只需要點選一次,結果手抖操作了很多次,重複執行就造成了額外的浪費。
?防抖函式的原理:在一定延遲時間內,連續觸發的事件合併只執行 最後 一次。技術原理是用閉包儲存一個延時執行函式setTimeout(func, delayTime)
返回變數,只要延遲時間delayTime
內新觸發的執行器,就會代替舊執行器。
?實現程式碼:
/****************************** 防抖函式 ******************************/
//引數func:需要防抖的函式
//引數delayTime:延時時長,單位ms
function debounce(func, delayTime) {
//用閉包路快取延時器id
let timer;
return function (...args) {
if (timer)
clearTimeout(timer); //清除-替換,把前浪拍死在沙灘上
timer = setTimeout(() => {
func.apply(this, args);
}, delayTime);
}
}
?適用場景:
- ✅ 提交按鈕,避免重複點選提交資料,只執行最後一次。
- ✅ 文字框輸入的響應:如基於輸入文字服務端聯想查詢,對輸入內容的服務端驗證等,防抖就可以避免沒必要的請求,節約資源。
- ✅ 連續觸發的事件,如視窗的
resize
事件、視窗的滾動scroll
事件,只處理最後一次。
?使用案例:滾動瀏覽器捲軸到末尾。
- 如果不用防抖函式,
scroll
事件頻繁觸發,共觸發了29次。 - 加上防抖函式,同樣的速度移動,只觸發了最後一次。
//移動瀏覽器捲軸到末尾,無防抖
window.addEventListener('scroll', print); //執行了29次
//加上防抖,延遲300ms
window.addEventListener('scroll', debounce(print, 300)); //執行了1次
let index = 0;
function print() {
console.log(index++);
}
03、為何要節流(throttle)?
節流(throttle)字面意思就是節約流量(throttle /ˈθrɑːtl/ 節流閥),一個小朋友一分鐘只能吃一勺飯,每分鐘餵了30勺,喂得太快要麼食物浪費了,要麼被噎到。
?節流函式的原理:一定時間內只執行一次事件,在一段時間intervalTime
內,不管觸發了多少次事件(大於1)都只執行一次。
- 因此首先需要判斷間隔時間,是否在間隔時間內。
- 具體執行的時機,可選擇首次,也可以選擇最後一次,或者首次+最後一次。
?實現程式碼:三種實現方式
- 實現1:單位時間內執行第一次(立即執行),節流後面的,基於時間間隔判斷。
- 實現2:單位時間內執行第一次(延遲執行),節流後面的,基於延時函式
setTimeout()
。 - 實現3:執行首次+最後一次,節流中間的,比較綜合全面的的實現方式!
// 實現1:單位時間內執行第一次(立即執行),節流後面的
function throttle(func, intervalTime = 100) {
let lastTime = 0;
return (...args) => {
let now = Date.now();
//首次呼叫會執行
if (now - intervalTime > lastTime) {
func.apply(this, args);
lastTime = now;
}
}
}
// 實現2:單位時間內執行第一次(延遲執行),節流後面的
const throttle2 = (func, intervalTime = 100) => {
// 定義falg,初試為true
let flag = true;
// 返回的函式是每次使用者實際呼叫的節流函式
return (...args) => {
const ctx = this;
// 如果flag為true,則執行定時器
if (flag) {
setTimeout(() => {
func.apply(ctx, args);
// 函式執行完畢後=true;
flag = true;
}, intervalTime);
}
//沒執行完成前都為false
flag = false;
};
}
// 實現3:執行首次+最後一次,節流中間的,比較綜合的節流方式!
function throttleMiddle(func, intervalTime = 100) {
let timer = null;
let startTime = 0;
return (...args) => {
const ctx = this;
const now = Date.now();
if (startTime && now < startTime + intervalTime) {
//替換前面的
clearTimeout(timer);
timer = setTimeout(() => {
startTime = now;
func.apply(ctx, args);
}, Math.max(intervalTime - (Date.now() - startTime), 0)); //剩餘等候時間
} else { //每輪首次會執行,立即執行
startTime = now;
func.apply(ctx, args);
}
}
}
// 節流-函式擴充套件,使用的throttleMiddle版本
Function.prototype.throttle = function (intervalTime = 100) {
let func = this;
let startTime, timer = null;
//這裡不能用箭頭函式,會導致this汙染
return function (...args) {
const ctx = this;
let now = Date.now();
if (startTime && now < startTime + intervalTime) {
clearTimeout(timer);
timer = setTimeout(() => {
func.apply(this, args);
startTime = now;
}, Math.max(intervalTime - (now - startTime), 0));
}
else {
startTime = now;
func.apply(ctx, args);
}
}
}
?適用場景:
- ✅ 定時秒殺、抽獎按鈕,執行多次提交,避免太過頻繁的提交把服務端搞崩了。
- ✅ 連續的UI互動,如DOM拖拽,在視窗的resize事件、視窗的滾動scroll事件中更新UI,如果用防抖會有卡頓的現象,更適合用節流。
?使用案例:滾動瀏覽器捲軸到末尾。
- 如果不用防抖函式,
scroll
事件頻繁觸發,共觸發了29次。 - 加上節流函式,同樣的速度移動,執行了4次,間隔均勻。
//移動瀏覽器捲軸到末尾
window.addEventListener('scroll', print); //執行了29次
//加上節流,延遲300ms
window.addEventListener('scroll', throttle(print, 300)); //執行了4次
let index = 0;
function print() {
console.log(index++);
}
©️版權申明:版權所有@安木夕,本文內容僅供學習,歡迎指正、交流,轉載請註明出處!原文編輯地址-語雀