廢話不多說,直奔主題。
什麼是throttle和debounce?
這兩個方法的主要目的多是用於效能優化。最常見的應用嚐盡就是在通過監聽resize、scroll、mouseover等事件時候的效能消耗。拿scroll來說,沒有處理時滑動一次滾動條scroll事件會觸發多次,如果其中涉及的程式碼偏重,那麼效能消耗肯定是非常大。使用節流和防抖就是去優化這種情況,通過同的使用場景決定使用的物件,接下來就對比一下兩者的區別。
throttle
在指定的delay(延遲時間)內,在delay間隔內多次呼叫,throttle會捨棄中間的所有呼叫操作,直到使用者停止行為後的delay後執行一次預期執行函式。這就稱為函式節流。
debounce
跟節流函式一樣,debounce也是在設定的delay間隔內多次呼叫執行函式的話,會捨棄這些操作。和throttle不同的是,debounce多了個強制執行時間引數mustRunDelay,不管前面捨棄了多少次操作,一旦時間tag>=mustRunDelay的話,執行函式一定會被呼叫一次。接下來上程式碼,更直觀。
原文參考原始碼出處
原文對於節流和防抖的描述有待商榷,但是最終的程式碼其實就是節流和防抖的綜合體。通過是否傳入mustRunDelay引數來區分。
function throttle (fn, delay, mustRunDelay = 0) {
let timer = null;
let tStart; //建立父級作用域時間tag
return function () {
const context = this;
const args = arguments;
const tCurr = +new Date();//子作用域時間tag
clearTimeout(timer);//每次執行,先清空定時器,這步操作便是delay時間內捨棄多餘操作的實現
if (!tStart) { // 首次給時間tag賦值
tStart = tCurr;
}
//這層判斷就是判斷是否達到強制執行的條件
if (mustRunDelay !== 0 && tCurr - tStart >= mustRunDelay) {
fn.apply(context, args);
tStart = tCurr;
} else {
timer = setTimeout(function () {
fn.apply(context, args);
}, delay);
}
};
}
忽略throttle的方法名,按照呼叫方式不同,他也可以是debounce。主要實現在於通過非同步操作的事件間隔,對於前後兩次呼叫方法打時間tag進行比較,用清空定時器的操作實現多餘呼叫操作的捨棄。還有一點是用了閉包的機制,便於管理tStart變數,因為閉包的關係,tStart記憶體不會被回收,否則需要在全域性定義該變數。
結尾
具體怎麼用呢,拿scroll事件舉個例子:
window.addEventListenr(`scroll`,throttle(scrollHandle,delay,mustRunDelay),false);
大概就這意思,使用時候根據場景使用,mustRunDelay>0?防抖:節流。