JavaScript 函數節流和函數去抖

概念

節流 (throttle) 讓一個函數不要執行的太頻繁,減少執行過快的調用,叫節流

去抖 (debounce) 去抖就是對於一定時間段的連續的函數調用,只讓其執行一次

throttle 應用場景

  • DOM 元素的拖拽功能實現(mousemove)
  • 射擊遊戲的 mousedown/keydown 事件(單位時間只能發射一顆子彈)
  • 計算鼠標移動的距離(mousemove)
  • Canvas 模擬畫板功能(mousemove
  • 搜索聯想(keyup
  • 監聽滾動事件判斷是否到頁面底部自動加載更多:給 scroll 加了 debounce 後,只有用戶停止滾動後,纔會判斷是否到了頁面底部;如果是 throttle 的話,只要頁面滾動就會間隔一段時間判斷一次

debounce 應用場景

  • 每次 resize/scroll 觸發統計事件
  • 文本輸入的驗證(連續輸入文字後發送 AJAX 請求進行驗證,驗證一次就好)

函數去抖的實現

我們以scroll事件爲例,探究如何是實現滾動一次窗口打印一個hello world 字符串。 如果不對其節流或者去抖:

window.onscroll = function () {
    console.log('hello world');
}

這樣每滾動一次,實際上會打印多個 hello world函數去抖背後的思路是指,某些代碼不可能在沒有間斷的情況下連續執行。創建一個定時器,在指定的時間間隔之後運行代碼。當第二次調用該函數時,它會清除前一次的定時器並設置另一個。如果前一個定時器已經執行過了,這個操作就沒有任何意義。然而,如果前一個定時器尚未執行,其實就是將其替換爲一個新的定時器。目的是只有在執行函數的請求停止了一段時間之後才執行。

《高程三》給出了最簡潔最經典的去抖代碼,如下:

function debounce(method, context) {
  clearTimeout(method.tId);
  method.tId = setTimeout(function() {
    method.call(context);
  }, 1000);
}

function print() {
  console.log('hello world');
}

window.onscroll = function() {
  debounce(print);
};

再做一些改動

function debounce(delay, action) {
    var tId;
    return function () {
        var context = this;
        var arg = arguments;
        if (tId) clearTimeout(tId);
        tId = setTimeout(function () {
            action.apply(context, arg);
        }, delay);
    }
}

window.onscroll = debounce(1000, print);

函數節流的實現

函數節流就是讓連續執行的函數,變爲固定時間段間斷地執行。 大概有兩種方式實現。

其一使用時間戳來判斷是否已經到回調執行時間,記錄上次執行的時間戳,然後每次觸發事件時執行回調,回調中判斷當前時間戳距離上次執行時間戳的時間間隔是否有*s,如果是,則執行,並更新上次執行的時間戳,如此循環。

var throttle = function(delay, action) {
    var last = 0;
    return function() {
        var curr = new Date();
        if (curr - last > delay) {
            action.apply(this, arguments);
            last = curr;
        }
    }
}

第二種方法是使用定時器,比如,當scroll事件剛觸發時,打印一個hello world ,然後設置一個1000ms的定時器,此後每次觸發scroll事件,觸發回調,如果已經存在定時器,則回調不執行方法,知道定時器出發,handler被清除,然後重新設置定時器。

var throttle = function(delay, action) {
    var timeout;
    var later = function () {
        timeout = setTimeout(function(){
            clearTimeout(timeout);
            // 解除引用
            timeout = null;
        }, delay);
    };
    later();
    if (!timeout) {
        action.apply(this, arguments);
        later();
    }
}

更新方法:

function throttlePro(delay, action) {
    var tId;
    return function () {
        var context = this;
        var arg = arguments;
        if (tId) return;
        tId = setTimeout(function () {
            action.apply(context, arg);
            clearTimeout(tId);
            // setTimeout 返回一個整數,clearTimeout 之後,tId還是那個整數,setInterval同樣如此
            tId = null;
        }, delay);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章