js之防抖與節流

瀏覽器的 resize、scroll、keypress、mousemove 等事件在觸發時,會不斷地調用綁定在事件上的回調函數,極大地浪費資源,降低性能。所以需要對這類事件進行調用次數的限制。

防抖(debounce)

作用是在短時間內多次觸發同一個函數,只執行最後一次,或者只在開始時執行。
以用戶拖拽改變窗口大小,觸發resize事件爲例,在這過程中窗口的大小一直在改變,所以如果我們在resize事件中綁定函數,這個函數將會一直觸發,而這種情況大多數情況下是無意義的,還會造成資源的大量浪費。這時候可以使用函數防抖來優化相關操作:

// debounce 函數接受一個函數和延遲執行的時間作爲參數
function debounce(fn, delay){
    // 維護一個 timer
    let timer = null;
    
    return function() {
        // 獲取函數的作用域和變量
        let context = this;
        let args = arguments;
        
        clearTimeout(timer);
        timer = setTimeout(function(){
            fn.apply(context, args);
        }, delay)
    }
}
function foo() {
  console.log('trigger');
}

// 在 debounce 中包裝我們的函數,過 2 秒觸發一次
window.addEventListener('resize', debounce(foo, 2000));

在resize事件上綁定處理函數,這時debounce函數會立即調用,實際上綁定的函數時debounce函數內部返回的函數。
每一次事件被觸發,都會清除當前的timer然後重新設置超時調用。
只有在最後一次觸發事件,才能在delay時間後執行。
我們也可以爲 debounce 函數加一個參數,可以選擇是否立即執行函數

function debounce(func, delay, immediate){
    var timer = null;
    return function(){
        var context = this;
        var args = arguments;
        if(timer) clearTimeout(timer);
        if(immediate){
            var doNow = !timer;
            timer = setTimeout(function(){
                timer = null;
            },delay);
            if(doNow){
                func.apply(context,args);
            }
        }else{
            timer = setTimeout(function(){
                func.apply(context,args);
            },delay);
        }
    }
}

節流(throttle)

類似於防抖,節流是在一段時間內只允許函數執行一次。
應用場景如:輸入框的聯想,可以限定用戶在輸入時,只在每兩秒鐘響應一次聯想。可通過時間戳和定時器來實現。

var throttle = function(func, delay){
    var prev = Date.now();
    return function(){
        var context = this;
        var args = arguments;
        var now = Date.now();
        if(now-prev>=delay){
            func.apply(context,args);
            prev = Date.now();
        }
    }
}
//定時器實現
var throttle = function(func, delay){
    var timer = null;

    return function(){
        var context = this;
        var args = arguments;
        if(!timer){
            timer = setTimeout(function(){
                func.apply(context, args);
                timer = null;
            },delay);
        }
    }
}

區別在於,使用時間戳實現的節流函數會在第一次觸發事件時立即執行,以後每過 delay 秒之後才執行一次,並且最後一次觸發事件不會被執行;而定時器實現的節流函數在第一次觸發時不會執行,而是在 delay 秒之後才執行,當最後一次停止觸發後,還會再執行一次函數。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章