Skip to content

防抖与节流

防抖和节流都用于处理高频触发的函数调用。它们解决的不是“能不能执行”,而是“应该按什么频率执行”。

两者的核心区别

  • 防抖:等一段时间都没有再次触发,才执行一次
  • 节流:在一段固定时间内,最多执行一次

可以先记成:

  • 防抖更像“等用户停下来”
  • 节流更像“控制执行频率”

什么场景适合防抖

更适合“用户停止输入后再处理”的场景:

  • 输入框搜索建议
  • 窗口尺寸变化后的重新布局
  • 表单频繁修改后的自动保存

什么场景适合节流

更适合“持续触发但不希望执行过于频繁”的场景:

  • 页面滚动
  • 鼠标移动
  • 拖拽
  • 高频 resize / pointermove 监听

防抖的基本实现

防抖的关键点是:每次触发都取消上一次计时,只保留最后一次。

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

  return function (...args) {
    clearTimeout(timer)
    timer = setTimeout(() => {
      fn.apply(this, args)
    }, delay)
  }
}

节流的基本实现

节流的关键点是:在时间窗口内直接忽略后续触发。

js
function throttle(fn, wait = 300) {
  let lastTime = 0

  return function (...args) {
    const now = Date.now()

    if (now - lastTime >= wait) {
      fn.apply(this, args)
      lastTime = now
    }
  }
}

为什么常看到 apply(this, args)

因为包装后的函数如果直接写成 fn(args),很容易把原来的:

  • this
  • 参数列表

都处理错。

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

  return function (...args) {
    clearTimeout(timer)
    timer = setTimeout(() => {
      fn.apply(this, args)
    }, delay)
  }
}

这能尽量保留调用时的上下文。

节流还有另一类实现

有些节流不是“窗口内完全忽略后续触发”,而是会保留最后一次参数,在窗口结束后补执行一次。

js
function throttle(fn, wait = 300) {
  let isWaiting = false
  let pendingArgs = null
  let pendingThis = null

  function later() {
    if (!pendingArgs) {
      isWaiting = false
      return
    }

    fn.apply(pendingThis, pendingArgs)
    pendingArgs = null
    pendingThis = null
    setTimeout(later, wait)
  }

  return function (...args) {
    if (isWaiting) {
      pendingArgs = args
      pendingThis = this
      return
    }

    fn.apply(this, args)
    isWaiting = true
    setTimeout(later, wait)
  }
}

这种写法更接近“既控频,又尽量不丢最后一次状态”。

两者最容易混淆的点

下面这种需求更偏防抖:

  • 用户停止输入 300ms 后再发请求

下面这种需求更偏节流:

  • 滚动过程中每 200ms 更新一次位置

如果把两者用反,常见结果是:

  • 搜索请求发得过多
  • 滚动反馈太迟钝

使用边界

防抖和节流都只是“包装调用频率”,不会改变原函数本身的业务逻辑。

还需要根据场景继续考虑:

  • 是否要立即执行第一次
  • 是否要保留最后一次调用
  • 是否需要取消能力
  • 异步请求是否还要结合中断控制

使用建议

  • “等停下来再处理”优先想到防抖。
  • “持续触发但限频”优先想到节流。
  • 包装函数时注意保留 this 和参数。
  • 需求如果涉及“首尾是否执行”,要先把行为规则说清楚再写代码。

基于 MIT 许可发布