淺談js函式節流和函式防抖

vianem發表於2019-05-04

什麼是函式節流和函式防抖

函式節流和函式防抖是一種優化方法,可用於減少高頻繁觸發任務(函式)的執行次數,達到減少資源佔用的目的。

  • 函式節流:任務在指定的間隔時間內只執行一次。
  • 函式防抖:只有在任務觸發的間隔大於等於指定的間隔時間,任務才會被執行。
  • 區別:在指定時間內,任務執行的次數不同。假設持續觸發一個任務1s,且任務間的觸發間隔為5ms。正常情況下,該任務觸發的次數為1s / 5ms = 200次。使用函式節流後(假設指定間隔時間為50ms),50ms內任務只執行一次,那麼該任務觸發的次數為1s / 50ms = 20次。使用函式防抖後(假設指定間隔為50ms),任務觸發間隔5ms < 指定間隔50ms,因此在這1s內,該任務執行次數為0次。任務在1s時停止觸發後,在1.05s時任務才執行1次。下面我們結合示例來看看函式節流和函式防抖的實現。

我們以滾動條的監聽事件為例,首先看看使用函式節流和函式防抖前的現象。

let count = 0;
$(document).scroll(function () {
    console.log(`觸發了${++count}次!`);
});
複製程式碼

淺談js函式節流和函式防抖
可以看到隨著滾動條的拖動,滾動條拖動的回撥函式被頻繁觸發,在回撥比較複雜的時候,頻繁觸發的回撥函式甚至會讓網頁出現掉幀的情況。而且有些時候,業務上我們並不需要這麼高頻繁的函式呼叫。這個時候我們就可以根據業務使用函式節流或函式防抖了。

函式節流

實現

let count = 0;
$(document).scroll(throttle(function () {
    console.log(`觸發了${++count}次!`);
}, 500));
function throttle(fn, wait) {
    let canRun = true;
    return function () {
        if (!canRun) {
            return;
        }
        canRun = false;
        setTimeout(() => {
            fn.apply(this, arguments);  //讓scroll的回撥函式的this保持一致
            canRun = true;
        }, wait);
    }
}
複製程式碼

我們指定了一個時間間隔500ms,規定500ms內滾動事件回撥函式只能執行一次。函式節流的實現要點是,使用閉包儲存是否可執行標記,如果可執行則設定一個延遲函式,在延遲函式中重置可執行標記。

淺談js函式節流和函式防抖

函式防抖

實現

let count = 0;
$(document).scroll(deBounce(function () {
    console.log(`觸發了${++count}次!`);
}, 500));
function deBounce(fn, interval) {
    let timer = null;
    return function () {
        if (timer) {
            clearTimeout(timer);
        }
        timer = setTimeout(() =>{
           fn.apply(this, arguments);
        }, interval);
    }
}
複製程式碼

我們指定一個時間間隔500ms,規定滾動事件的回撥函式,只有在事件觸發的間隔大於500ms時回撥函式才執行。函式防抖的實現要點是,使用閉包儲存一個延遲函式編號(便於清空延遲函式),在事件觸發的時候,清空這個延遲函式,並且設定新的延遲函式。

淺談js函式節流和函式防抖

關於fn.apply(this, arguments)

上文函式節流和函式防抖的實現中,呼叫fn的時候都是用的fn.apply(this, arguments)呼叫,而不是fn()直接呼叫。主要原因是為了fn函式內的this與原本的事件回撥函式繫結的this保持一致。 setTimeout()呼叫的程式碼執行在與所在函式完全分離的執行環境上。這會導致,這些程式碼中包含的 this 關鍵字會指向 window (或全域性)物件。因此在setTimeout中使用箭頭函式(箭頭的this繫結的是當前的詞法作用域)此時的詞法作用域為scroll事件回撥函式中繫結的this(jquery中強制this指向dom元素),再將fn函式內的this繫結為這個this。我們以函式防抖為例看看這兩種的區別。

$(document).scroll(deBounce(function () {
    console.log(this);  //#document
}, 500));
function deBounce(fn, interval) {
    let timer = null;
    return function () {
        if (timer) {
            clearTimeout(timer);
        }
        timer = setTimeout(() =>{
           fn.apply(this, arguments);  
        }, interval);
    }
}

複製程式碼
$(document).scroll(deBounce(function () {
    console.log(this);  //Window
}, 500));
function deBounce(fn, interval) {
    let timer = null;
    return function () {
        if (timer) {
            clearTimeout(timer);
        }
        timer = setTimeout(() =>{
           fn(); 
        }, interval);
    }
}
複製程式碼
$(document).scroll(deBounce(function () {
    console.log(this);  //Window
}, 500));
function deBounce(fn, interval) {
    let timer = null;
    return function () {
        if (timer) {
            clearTimeout(timer);
        }
        timer = setTimeout(function () {
           fn.apply(this, arguments); 
        }, interval);
    }
}
複製程式碼

希望本文對大家有所幫助,互相學習,一起提高。轉載請註明原帖。

相關文章