Js 函數防抖和節流

咱們前端在實際開發過程當中,常常會遇到以下問題:前端

  • 頁面的 scroll 事件
  • input 輸入檢測事件
  • 高頻點擊提交

若是不作任何處理的話,頁面可能會卡頓,性能較低。這時候就須要函數防抖和節流來出馬啦。bash

1. 函數防抖

1.1 定義

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

例子:電梯是在等人進入10秒後自動關閉。若是電梯進人後10s內再次有人進入,則又得等10秒鐘電梯纔會自動關閉。若是中間一直進人,就一直不會關閉。函數

1.2 代碼實現

思路:用定時器執行函數,判斷若是存在定時器任務,則清空定時器,從新開啓一個新的定時器性能

簡易代碼:ui

// fn 是須要執行的函數, interval 是等待時間間隔
function debounce(fn, interval = 300) {
  let timeout = null
  return function() {
    clearTimeout(timeout)
    timeout = setTimeout(()=> {
      fn.apply(this, arguments)
    }, interval)
  }
}
複製代碼

可是這一版防抖有個缺點,就是第一次不會執行,得等到 interval 後纔會執行,若是須要首次執行,可修改代碼以下:this

// immediate 爲 true 表明須要當即執行 fn, 爲 false 表明不須要
function debounce(fn, interval = 1000, immediate) {
  let timeout = null
  let first = true
  return function() {
    clearTimeout(timeout)
    if (immediate && first) {
      fn.apply(this, arguments)
      first = false
    }
    timeout = setTimeout(()=> {
      fn.apply(this, arguments)
    }, interval)
  }
}
複製代碼

1.3 使用

document.querySelector('body').addEventListener('mousemove', debounce((e) => {
  console.log(e.clientY)
}, 300, true))
複製代碼

如圖,對 mousemove事件防抖:spa

debounce

2. 函數節流

2.1 定義

規定一個單位時間,在這個單位時間內,只能有一次觸發事件的回調函數執行,若是在同一個單位時間內某事件被觸發屢次,只有一次能生效。3d

例子: 在頁面滾動時,會不斷地觸發 scroll 事件,實際上咱們不須要這麼頻繁地去執行函數,這是就能夠設置一個間隔,好比說 300ms,在 300ms 這個時間內,scroll 內的函數只能執行一次。code

2.2 代碼實現

思路 1:用時間軸來判斷,若是事件觸發時間與上一次函數執行時間之差大於規定時間,則執行一次,反之不執行

代碼 1:

// fn 是須要執行的函數, interval 是等待時間間隔
function throttle(fn, interval = 1000) {
  let pre = 0
  return function() {
    const now = Date.now()
    if (now - pre > interval) {
      fn.apply(this, arguments)
      pre = now
    }
  }
}
複製代碼

這個函數特色是:會馬上執行,中止觸發後就不會執行。

思路 2:用定時器,在定時器未執行的時間內,再次觸發的函數將再也不執行。

代碼 2:

// fn 是須要執行的函數, interval 是等待時間間隔
function throttle(fn, interval = 1000) {
  let canRun = true
  return function() {
    if (!canRun) {
      return
    }
    canRun = false
    setTimeout(() => {
      fn.apply(this, arguments)
      canRun = true
    }, interval)
  } 
}
複製代碼

這個函數特色是:等待 interval 時間間隔後才執行,中止觸發後會再執行一次。

把思路 1 和思路 2 結合起來,用變量來控制第一次是否執行及中止觸發後是否再執行一次:

// options 做爲第三個參數,
// options.leading 爲 true 時表示第一次就執行,
// options.trailing 爲 true 是表示中止觸發時再執行一次

function throttle(fn, interval = 1000, options = {}) {
  let pre = 0
  let canRun = true
  let first = options.leading || false
  return function() {
    if (first) {
      fn.apply(this, arguments)
        first = false
    }
    if (options.trailing) {
      if (!canRun) {
        return
      }
      canRun = false
      setTimeout(() => {
        fn.apply(this, arguments)
        canRun = true
      }, interval)
    } else {
      const now = Date.now()
      if (now - pre > interval) {
        if (pre) {
          fn.apply(this, arguments)
        }
        pre = now
      }
    }
  }
}
複製代碼

2.3 使用

document.querySelector('body').addEventListener('mousemove', throttle((e) => {
  console.log(e.clientY)
}, 1000, {
  leading: true,
  trailing: true
}))
複製代碼

如圖,對 mousemove事件節流:

throttle
相關文章
相關標籤/搜索