前端百題——通俗易懂的防抖與節流

 

性能一直是前端老生常談的一個話題,其中有一個性能問題就是咱們會頻繁的觸發一些事件,例如mousemove、scroll、resize等,雖然瀏覽器已經對這些事件的觸發作了一些優化,可是若是在很短的時間內頻繁的觸發仍然會影響性能,這個時候就須要今天的主角:防抖和節流,利用它們來進行優化,提升性能。前端

21.1 防抖

img

21.1.1 定義

防抖就是將屢次高頻操做優化爲只在最後一次執行(某個函數在某段時間內,不管觸發了多少次回調,都只執行最後一次)。一般的使用場景是:用戶輸入,只需在輸入完成後作一次輸入校驗便可。瀏覽器

21.1.2 實現

防抖是將屢次操做合併爲一次操做完成,其原理就是維護一個計時器,在規定的時間後觸發函數,可是在該規定時間內再次觸發的話就會取消以前的定時器而從新設置,從而保證了只有最後一次操做可以被觸發。其實現步驟以下所示:閉包

  1. 利用閉包保存一個timer變量,而後返回一個函數(這個返回的函數就是後續頻繁觸發操做中調用的函數);
  2. 根據標誌位判斷是否第一次須要當即執行(由於有些狀況是須要首次調用函數當即執行的,若沒有該參數,就會在定時器到了以後纔會執行);
  3. 當有新的觸發時,若存在定時器,則清空該定時器;
  4. 設定一個新的定時器,從新計時。
function debounce(fn, wait, immediate) {
        let timer = null;
        return function (...args) {
            // 當即執行的功能(timer爲空表示首次觸發)
            if (immediate && !timer) {
                fn.apply(this, args);
            }

            // 有新的觸發,則把定時器清空
            timer && clearTimeout(timer);
            // 從新計時
            timer = setTimeout(() => {
                fn.apply(this, args);
            }, wait);
        }
    }
複製代碼

21.1.3 效果預覽

防抖.gif

觀察效果圖能夠驗證上述的理論知識:app

  1. 防抖以後輸出內容的頻次下降了;
  2. 防抖以後,其在超過必定時間以後纔會輸出內容。

21.2 節流

img

21.2.1 定義

節流就是每隔一段時間後執行一次,也就是下降頻率,將高頻操做優化成低頻操做。一般使用場景:滾動條事件、resize事件、動畫等,一般每隔100-500ms執行一次便可。ide

21.2.2 實現

節流函數的實現方式有兩種:定時器版本、時間戳版本,這二者各有千秋,下面來簡要實現一下。函數

21.2.2.1 定時器版本性能

定時器版本的節流函數其重點是利用閉包保存timer變量,具備兩個特色:優化

  1. n秒後纔會執行第一次(定時器到了時間後纔會觸發);
  2. 中止觸發後節流函數還會執行一次(由於該函數是延遲執行的,當中止觸發時其任務已經到了隊列中,因此中止後還會執行一次)。
// 定時器版本
function throttle(fn, wait) {
    let timer = null;
    return function(...args) {
        if (!timer) {
            timer = setTimeout(() => {
                fn.apply(this, args);
                timer = null;
            }, wait)
        }
    }
}
複製代碼

21.2.2.2 時間戳版本動畫

時間戳版本的節流函數重點是利用閉包保存上一次的時間previous,具備兩個特色:this

  1. 開始觸發後會當即執行(由於previous開始會被賦值爲0);
  2. 中止觸發後再也不執行(由於該函數是同步任務,在觸發的時候就會進行相應的判斷,因此就不存在中止觸發後再執行的狀況)。
// 時間戳版本
function throttle(fn, wait) {
    // 上一次執行時間
    let previous = 0;
    return function(...args) {
        // 當前時間
        let now = +new Date();
        if (now - previous > wait) {
            previous = now;
            fn.apply(this, args);
        }
    }
}
複製代碼

21.2.3 效果預覽

節流.gif

觀察效果圖能夠驗證上述的理論知識:

  1. 節流確實下降了內容的輸出頻率,將高頻變爲低頻;
  2. 時間戳版本的節流函數在首次會輸出內容,可是最後一次的內容不會輸出(謹慎使用);
  3. 定時器版本的節流函數確實不會馬上打印內容,而是超過必定時間以後纔會打印;此外,其最後輸入的內容會被打印出來。
相關文章
相關標籤/搜索