節流和防抖

防抖和節流屬於性能優化,在進行窗口的resize、滾動條scroll,輸入框內容校驗等操做時,若是事件處理函數調用的頻率無限制,會加劇瀏覽器的負擔,致使用戶體驗很是糟糕。瀏覽器

此時咱們能夠採用debounce(防抖)和throttle(節流)的方式來減小調用頻率,同時又不影響實際效果。性能優化

函數防抖(debounce)

無論事件觸發頻率多高,必定在事件觸發n秒後才執行,若是你在一個事件觸發的 n 秒內又觸發了這個事件,就以新的事件的時間爲準,n秒後才執行。對於短期內連續觸發的事件,防抖的含義就是讓某個時間期限(如上面的1000毫秒)內,事件處理函數只執行一次。markdown

實現:

  • 實現的關鍵就在於setTimeout這個函數,因爲還須要一個變量來保存計時,考慮維護全局純淨,能夠藉助閉包來實現。
  • 裏面的setTimeout則用的箭頭函數,這樣作的意義是讓this的指向準確,this的真實指向並不是debounce的調用者,而是返回閉包的調用者。
  • 對傳入閉包的參數進行透傳
function debounce(event, time) {
    let timer = null;
    return function(...args) {
      clearTimeout(timer);
      timer = setTimeout(()=> {
        event.apply(this, args);
      }, time)
    }
 }
 // 若是須要當即執行, 加一個flag 標誌, 定時器變量`timer`爲空時,說明是第一次執行,咱們當即執行它。
 
 function debounce(fn, time, flag) {
   let timer = null;
   return function(...args) {
    clearTimeout(timer);
    if(flag && !timer) {
       fn.apply(this, args)
    }
    timer = setTimeout(()=> {
       fn.apply(this, args);
    }, time)
   }
 }

複製代碼

應用場景:

一、窗口的resize閉包

window.addEventListener('resize', debounce(handleResize, 200))
複製代碼

二、滾動條scrollapp

function showTop() {
  const scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
  console.log(scrolltop);
}
window.addEventListener('scroll', debounce(showTop, 1000))
複製代碼

三、輸入框內容校驗, 搜索框函數

debounce(valicator/fetchSelectData, 1000)
複製代碼

函數節流(throttle)

當持續觸發事件時,保證必定時間段內只調用一次事件處理函數.性能

節流比如水龍頭放水,按照必定規律在某個時間間隔內一滴一滴的往下滴, 不開閥放水浪費;fetch

函數節流主要有兩種實現方法: 時間戳和定時器;優化

時間戳實現:

首刻發生,尾刻不發生,中間正常this

function throttle(event, time) {
    let pre = 0;
    return function(...args) {
       if(Date.now() - pre > time) {
          pre = Date.now();
          event.apply(this, args);
       }
    } 
}

複製代碼

定時器實現

首刻不發生,尾刻發生,中間正常;

function throttle(event, time) {
  let timer = null;
  return function(...args) {
    if(!timer) {
       timer = setTimeout(()=>{
         clearTimeout(timer)
         timer=null;
         event.apply(this, args);
       }, time);
    }
  }
}
複製代碼

結合

function throttle(event, time) {
  let pre = 0, timer = null;
  return function(...args) {
     if(Date.now() - pre > time) {
         pre= Date.now();
         timer = null
         clearTimeout(timer);
         event.apply(this, args)
     } else if(!timer) {
        timer = setTimeout(()=>{
          event.apply(this, args);
        }, time);
     }
  }
}
複製代碼