每日一题深度解答:什么是防抖和节流?有什么区别?如何实现?

防抖(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每日一道面试题详解函数

相关文章
相关标签/搜索