每日一題深度解答:什麼是防抖和節流?有什麼區別?如何實現?

防抖(debounce)

定義

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

應用場景

  • 搜索框輸入發送AJAX請求
  • 窗口縮放,每次resize/scroll觸發事件

用例

好比有一個關鍵詞搜索的input框,用戶輸入後觸發keyup事件向後端發送一個請求:html

<div>
  <input id="input" type="text">
</div>

<script> const input = document.getElementById('input') input.addEventListener('keyup', (e) => { console.log(e.target.value) }) </script>
複製代碼

測試後能夠發現,每次輸入都會有打印結果,若是是請求後端接口,那麼不只會形成極大的浪費,並且實際應用中,用戶也是輸出完整的字符後,纔會請求。下面咱們優化一下:java

function debounce(fn, delay) {
    let timer = null

    return function(...args) {
      const context = this
      if(timer) {
        clearTimeout(timer)
      }
      const _agrs = args.join(',')
      timer = setTimeout(fn.bind(context, _agrs), delay)
    }
  }

  const consoleDebounce = debounce(console.log, 500)

  const input = document.getElementById('input')

  input.addEventListener('keyup', (e) => {
    consoleDebounce(e.target.value)
  })
複製代碼

在運行一次後能夠看到,當在頻繁的輸入時,並不會打印結果,只有當你在指定間隔內沒有輸入時,纔會執行函數。若是中止輸入可是在指定間隔內又輸入,會從新觸發計時。git

完整實現一個通用防抖函數

function debounce(fn, delay = 500, immediate = false) {
  let timer = null

  return function(...args) {
    const context = this
    // 是否當即執行一次
    if (immediate && !timer) {
      fn.apply(context, args)
    }

    if (timer) {
      clearTimeout(timer)
    }

    timer = setTimeout(() => {
      fn.apply(context, args)
    }, delay)
  }
}
複製代碼

節流(throttle)

定義:

指連續觸發事件可是在 n 秒中只執行一次函數。 節流會稀釋函數的執行頻率github

應用場景

  • DOM 元素的拖拽功能實現(mousemove)
  • 計算鼠標移動的距離(mousemove)
  • 監聽滾動事件判斷是否到頁面底部自動加載更多內容
  • 按鈕點擊事件(屢次點擊 n 秒內只生效一次)

用例

一個 button 按鈕,每次點擊須要提交表單。面試

<div>
  <button id="button">提交</button>
</div>

<script> const button = document.getElementById('button') button.addEventListener('click', (e) => { console.log('click') }) </script>
複製代碼

上面代碼的問題是,當咱們快速點擊 button 時,每次點擊都會打印 'click',下面用節流函數優化一下。後端

const conThrottle = throttle(console.log, 2000)

  const button = document.getElementById('button')
  button.addEventListener('click', (e) => {
    conThrottle(1)
  })


  function throttle(fn, time) {
    let timer = null
    let flag = true

    return function(...args) {
      let context = this
      if (flag) {
        fn.apply(context, args)
        flag = false
        timer = null
      }

      if (!timer) {
        timer = setTimeout(() => {
          flag = true
        }, time)
      }
    }
  }
複製代碼

再次測試,能夠發現無論咱們點擊的速度多麼快,在每 2 秒內,函數只會執行 1 次。app

完整實現時間戳版本 throttle 函數

function throttle(fn, time) {
    let startTime = 0

    return function(...args) {
      let context = this
      let endTime = Date.now()

      if (endTime - startTime >= time) {
        fn.apply(context, args)
        startTime = endTime
      }
    }
  }
複製代碼

查看原文 關注github每日一道面試題詳解函數

相關文章
相關標籤/搜索