web学习笔记编程技术学习与分享
首页 / 前端开发 / 正文

防抖与节流:从手写实现到边界条件

2026 年 07 月 28 日约 3 分钟阅读 前端开发

搜索框输入、窗口缩放、滚动加载都会高频触发回调。防抖(debounce)与节流(throttle)是两种不同的收敛策略,选错会让交互变味。

语义差异

  • 防抖:事件停止触发一段时间后才执行。适合「等用户停下来」的场景,如搜索联想、表单校验。
  • 节流:固定时间内最多执行一次。适合「过程中要持续反馈」的场景,如滚动进度、拖拽。

防抖的实现

function debounce(fn, wait = 300, immediate = false) {
  let timer = null;
  return function (...args) {
    const callNow = immediate && !timer;
    clearTimeout(timer);
    timer = setTimeout(() => {
      timer = null;
      if (!immediate) fn.apply(this, args);
    }, wait);
    if (callNow) fn.apply(this, args);
  };
}

注意 this 与参数要通过 apply 透传,否则在类方法或事件处理器里会丢上下文。

节流的实现

function throttle(fn, wait = 200) {
  let last = 0, timer = null;
  return function (...args) {
    const now = Date.now();
    const remain = wait - (now - last);
    if (remain <= 0) {
      last = now;
      fn.apply(this, args);
    } else if (!timer) {
      timer = setTimeout(() => {
        last = Date.now();
        timer = null;
        fn.apply(this, args);
      }, remain);
    }
  };
}

这个版本保证最后一次触发不会丢,比简单的「时间戳判断」更符合预期。

三个常见错误

一、在渲染函数里创建防抖函数。每次渲染都生成新实例,防抖完全失效。要在组件外或用 useRef 保存。

二、卸载时没有清理定时器。组件销毁后回调仍会执行,可能访问已释放的 DOM。

三、用防抖处理滚动事件。用户滚动期间毫无反馈,体验反而更差——这种场景应该用节流或 requestAnimationFrame。

什么时候不用

如果事件本身频率不高(比如点击提交),加防抖只会增加延迟,还可能导致用户以为没点上。判断依据是:回调的实际开销是否值得用延迟换频率。

JavaScript性能交互