關於js節流函式throttle和防抖動debounce

zhuangjunkun發表於2019-02-16

廢話不多說,直奔主題。

什麼是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?防抖:節流。

相關文章