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

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

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

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

咱們以滾動條的監聽事件爲例,首先看看使用函數節流和函數防抖前的現象。bash

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

能夠看到隨着滾動條的拖動,滾動條拖動的回調函數被頻繁觸發,在回調比較複雜的時候,頻繁觸發的回調函數甚至會讓網頁出現掉幀的狀況。並且有些時候,業務上咱們並不須要這麼高頻繁的函數調用。這個時候咱們就能夠根據業務使用函數節流或函數防抖了。

函數節流

實現閉包

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內滾動事件回調函數只能執行一次。函數節流的實現要點是,使用閉包存儲是否可執行標記,若是可執行則設置一個延遲函數,在延遲函數中重置可執行標記。app

函數防抖

實現dom

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時回調函數才執行。函數防抖的實現要點是,使用閉包存儲一個延遲函數編號(便於清空延遲函數),在事件觸發的時候,清空這個延遲函數,而且設置新的延遲函數。函數

關於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);
    }
}
複製代碼

但願本文對你們有所幫助,互相學習,一塊兒提升。轉載請註明原帖。優化

相關文章
相關標籤/搜索