函数的节流与防抖

521 阅读3分钟

1. 事件频繁触发可能造成的问题?

  1. 一些浏览器时间:window.onresizewindow.mousemove 等,触发的频率非常高,会造成浏览器性能问题(浪费)
  2. 如果向后台发送请求,频繁触发,对服务器造成不必要的压力

2. 如何限制事件处理函数频繁调用?

  • 函数节流
  • 函数防抖

3. 函数节流(throttle)

  • 理解:
    • 在函数需要频繁触发时:(事件频繁触发,函数频繁调用)
      • 函数执行一次后,只有大于设定的执行周期后才会执行第二次
    • 适合多次事件按时间做平均分配触发
      • 固定的时间内,只有一次有效,第二次要大于指定的时间才会继续触发
    • 间隔指定的时间就调用一下
  • 场景:(需要中间的变化过程就用节流)
    • 窗口调整 (resize)
    • 页面滚动 (scroll)
    • DOM 元素的拖拽功能实现 (mousemove)
    • 抢购疯狂点击 (mousedown)

4. 函数防抖(debounce)

  • 理解:
    • 在函数需要频繁触发时:(事件频繁触发,函数频繁调用)
      • 在规定时间内,只让最后一次生效,前面的不生效。
    • 适合多次事件一次响应的情况
      • 下一次的点击和这一次的点击要大于限定的间隔时间,那么才会触发
    • 最后一次点击完毕之后,过了限定的间隔时间才会调用
      • 前面的调用都取消(忽略)了
  • 场景:(只看最终结果)
    • 实时搜索联想 (keyup)
    • 文本输入的验证(连续输入文字后发送 AJAX 请求验证,验证一次就好)
    • 判断 scroll 是否滑到底部,滚动事件 + 函数防抖

实际开发中看需求选用

<body>
  <button id="throttle">测试函数节流</button>
  <button id="debounce">测试函数防抖</button>
  <script>
    /* 处理点击事件的回调函数 */
    function handleClick() { // 处理事件的回调
      console.log('处理点击事件')
    }

    /* 实现函数节流的函数 */
    function throttle(callback, delay) {
      let startTime = 0 // 必须保证第一次点击立即调用
      return function () { // 它的 this 是谁就得让 callback() 中的 this 是谁,它接收的所有实参都直接交给 callback()
        console.log('throttle 事件...')
        const currentTime = Date.now()
        if (currentTime - startTime > delay) { // 从第二次点击开始,需要 `当前时间与初始时间的时间差` 超过 `指定的间隔时间`
          // 一旦超过,立即调用
          callback.apply(this, arguments)
          startTime = currentTime
        }
      }
    }

    document.getElementById('throttle').onclick = throttle(handleClick, 1000)

    /* 实现函数防抖的函数 */
    function debounce(callback, delay) {
      return function () {
        console.log('debounce 事件...')
        // 预先存储 this 和 参数
        const that = this
        const args = arguments
        // 调用之前,先清除上一次待执行的定时器任务
        if (callback.timeoutId) {
          clearTimeout(callback.timeoutId)
        }
        // 每隔限定的间隔时间,启动一个新的延迟定时器,去准备调用 callback
        callback.timeoutId = setTimeout(function () {
          callback.apply(that, args)
          // 如果定时器的回调函数执行完毕了,立即删除标记
          delete callback.timeoutId
        }, delay)
      }
    }

    document.getElementById('debounce').onclick = debounce(handleClick, 1000)
  </script>
</body>