标准答案

  1. 防抖:debounce 是事件连续触发时先不执行,等最后一次触发后经过指定时间才执行。它适合搜索输入联想、窗口 resize 后重新计算、表单校验等“只关心最终结果”的场景。
  2. 节流:throttle 是事件持续触发时,保证固定时间内最多执行一次。它适合滚动监听、拖拽位置更新、进度同步、埋点上报等“需要持续反馈但不能太频繁”的场景。
  3. 场景选择:搜索框一般用防抖,避免每输入一个字符就请求;滚动和拖拽一般用节流,保证页面持续响应;按钮提交更准确地说是防重复提交,要结合请求中状态、禁用按钮和接口幂等,而不只是套一个 debounce。
  4. 实现要点:手写时要保留 this 和参数,并考虑立即执行、尾部执行、取消执行等选项。工程里也可以直接使用成熟工具函数,重点是选对策略和处理边界。

题目解析

防抖和节流的区别要从“业务想要什么反馈”出发。防抖等待用户停止动作,适合最终结果;节流控制执行频率,适合持续反馈。

搜索联想用防抖,是因为用户还在输入时请求价值不高;滚动和拖拽用节流,是因为用户持续操作时仍需要阶段性更新 UI 或上报状态。

按钮提交不是简单的防抖题。真正可靠的防重复提交要结合 loading 状态、按钮禁用、请求取消、接口幂等和服务端防重。只靠前端 debounce,刷新页面或并发请求仍可能绕过。

手写实现的考点通常是 this、参数、leading、trailing、cancel。基础版本能说明核心思路即可,更完整的工程版本可以交给工具库,但要知道边界。

代码示例

下面是保留 this 和参数的核心版 debounce 与 throttle。

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

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

function throttle(fn, delay) {
  let lastTime = 0

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

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

常见误区

  • 把防抖和节流的触发时机说反。
  • 只在前端防抖按钮提交,不处理接口幂等和请求中状态。
  • 手写实现时丢失 this 和参数。

作者信息