完全弄懂函數防抖和函數節流

原博客地址,歡迎star前端

函數防抖和節流

函數防抖和函數節流:優化高頻率執行js代碼的一種手段,js中的一些事件如瀏覽器的resize、scroll,鼠標的mousemove、mouseover,input輸入框的keypress等事件在觸發時,會不斷地調用綁定在事件上的回調函數,極大地浪費資源,下降前端性能。爲了優化體驗,須要對這類事件進行調用次數的限制。git

函數防抖

在事件被觸發n秒後再執行回調,若是在這n秒內又被觸發,則從新計時。

根據函數防抖思路設計出初版的最簡單的防抖代碼:github

var timer; // 維護同一個timer
function debounce(fn, delay) {
    clearTimeout(timer);
    timer = setTimeout(function(){
        fn();
    }, delay);
}

用onmousemove測試一下防抖函數:瀏覽器

// test
function testDebounce() {
    console.log('test');
}
document.onmousemove = () => {
  debounce(testDebounce, 1000);
}

上面例子中的debounce就是防抖函數,在document中鼠標移動的時候,會在onmousemove最後觸發的1s後執行回調函數testDebounce;若是咱們一直在瀏覽器中移動鼠標(好比10s),會發現會在10 + 1s後纔會執行testDebounce函數(由於clearTimeout(timer)),這個就是函數防抖。閉包

在上面的代碼中,會出現一個問題,var timer只能在setTimeout的父級做用域中,這樣纔是同一個timer,而且爲了方便防抖函數的調用和回調函數fn的傳參問題,咱們應該用閉包來解決這些問題。app

優化後的代碼:frontend

function debounce(fn, delay) {
    var timer; // 維護一個 timer
    return function () {
        var _this = this; // 取debounce執行做用域的this
        var args = arguments;
        if (timer) {
            clearTimeout(timer);
        }
        timer = setTimeout(function () {
            fn.apply(_this, args); // 用apply指向調用debounce的對象,至關於_this.fn(args);
        }, delay);
    };
}

測試用例:前端性能

// test
function testDebounce(e, content) {
    console.log(e, content);
}
var testDebounceFn = debounce(testDebounce, 1000); // 防抖函數
document.onmousemove = function (e) {
    testDebounceFn(e, 'debounce'); // 給防抖函數傳參
}

使用閉包後,解決傳參和封裝防抖函數的問題,這樣就能夠在其餘地方隨便將須要防抖的函數傳入debounce了。函數

函數節流

每隔一段時間,只執行一次函數。
  • 定時器實現節流函數:

請仔細看清和防抖函數的代碼差別post

function throttle(fn, delay) {
    var timer;
    return function () {
        var _this = this;
        var args = arguments;
        if (timer) {
            return;
        }
        timer = setTimeout(function () {
            fn.apply(_this, args);
            timer = null; // 在delay後執行完fn以後清空timer,此時timer爲假,throttle觸發能夠進入計時器
        }, delay)
    }
}

測試用例:

function testThrottle(e, content) {
    console.log(e, content);
}
var testThrottleFn = throttle(testThrottle, 1000); // 節流函數
document.onmousemove = function (e) {
    testThrottleFn(e, 'throttle'); // 給節流函數傳參
}

上面例子中,若是咱們一直在瀏覽器中移動鼠標(好比10s),則在這10s內會每隔1s執行一次testThrottle,這就是函數節流。

函數節流的目的,是爲了限制函數一段時間內只能執行一次。所以,定時器實現節流函數經過使用定時任務,延時方法執行。在延時的時間內,方法若被觸發,則直接退出方法。從而,實現函數一段時間內只執行一次。

根據函數節流的原理,咱們也能夠不依賴 setTimeout實現函數節流。

  • 時間戳實現節流函數:
function throttle(fn, delay) {
    var previous = 0;
    // 使用閉包返回一個函數而且用到閉包函數外面的變量previous
    return function() {
        var _this = this;
        var args = arguments;
        var now = new Date();
        if(now - previous > delay) {
            fn.apply(_this, args);
            previous = now;
        }
    }
}

// test
function testThrottle(e, content) {
    console.log(e, content);
}
var testThrottleFn = throttle(testThrottle, 1000); // 節流函數
document.onmousemove = function (e) {
    testThrottleFn(e, 'throttle'); // 給節流函數傳參
}

其實現原理,經過比對上一次執行時間與本次執行時間的時間差與間隔時間的大小關係,來判斷是否執行函數。若時間差大於間隔時間,則馬上執行一次函數。並更新上一次執行時間。

異同比較

相同點:

  • 均可以經過使用 setTimeout 實現。
  • 目的都是,下降回調執行頻率。節省計算資源。

不一樣點:

  • 函數防抖,在一段連續操做結束後,處理回調,利用clearTimeout 和 setTimeout實現。函數節流,在一段連續操做中,每一段時間只執行一次,頻率較高的事件中使用來提升性能。
  • 函數防抖關注必定時間連續觸發的事件只在最後執行一次,而函數節流側重於一段時間內只執行一次。

常見應用場景

函數防抖的應用場景

連續的事件,只需觸發一次回調的場景有:

  • 搜索框搜索輸入。只需用戶最後一次輸入完,再發送請求
  • 手機號、郵箱驗證輸入檢測
  • 窗口大小Resize。只需窗口調整完成後,計算窗口大小。防止重複渲染。

函數節流的應用場景

間隔一段時間執行一次回調的場景有:

  • 滾動加載,加載更多或滾到底部監聽
  • 谷歌搜索框,搜索聯想功能
  • 高頻點擊提交,表單重複提交

文檔中出現的源代碼都在這裏: 防抖節流

參考資料:

淺析函數防抖與函數節流
JavaScript專題系列-防抖和節流
7分鐘理解JS的節流、防抖及使用場景
防抖、節流

可能這些參考資料中有某些錯誤,可是表示感謝,博客中有些內容用了裏面的資料。

相關文章
相關標籤/搜索