防抖适用于用户操作停止后再执行,如搜索框输入;节流适用于固定频率执行且需及时响应,如滚动监听。二者均需处理this绑定、参数传递及定时器清理,推荐使用Lodash或React自定义Hook。

防抖和节流不是 JavaScript 语言层面的必需机制,而是应对高频事件(比如 resize、scroll、input)导致函数被过度调用的实际需求。不加控制时,一个快速拖拽滚动条的动作可能触发上百次 scroll 回调,直接卡死 UI 或浪费计算资源。
防抖适用于“等用户彻底停下来再执行”的场景
典型例子是搜索框的实时建议:用户每敲一个字就发请求,既浪费带宽又让后端压力陡增;改成防抖后,只在用户停顿(比如 300ms)后再发起最后一次查询。
-
debounce的核心是每次触发都清除前一次定时器,重置倒计时 - 必须区分「立即执行」和「非立即执行」:传入
immediate = true可让第一次触发立刻运行,后续触发则重新计时 - 注意 this 和参数绑定:直接传入
func而不处理上下文,回调里this会丢失,推荐用func.apply(context, args)
function debounce(func, wait, immediate = false) {
let timeout;
return function(...args) {
const later = () => {
timeout = null;
if (!immediate) func.apply(this, args);
};
const callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(this, args);
};
}
节流适用于“固定频率执行,但不能丢弃所有中间状态”的场景
比如 canvas 绘图跟随鼠标移动,或监听页面滚动位置做吸顶判断。这类逻辑不需要每帧都响应,但也不能等到用户停下才动——否则体验会明显滞后。
-
throttle常见实现有「定时器版」和「时间戳版」:前者更稳定,后者更省资源但首次触发可能延迟 - 定时器版会在上一次执行结束后的 wait 时间内拒绝新调用;时间戳版则靠记录上次执行时间,差值不足 wait 就跳过
- 节流函数内部需保存
context和args,否则多次触发时参数会错乱
function throttle(func, wait) {
let timeout = null;
return function(...args) {
if (!timeout) {
timeout = setTimeout(() => {
func.apply(this, args);
timeout = null;
}, wait);
}
};
}
实际项目中别手写,优先用 Lodash 或封装好的 Hook
手写容易漏掉边界情况:比如未清理定时器导致内存泄漏、未处理 this 绑定、未支持取消(cancel 方法)、未兼容服务端渲染(SSR 中无 setTimeout)。
立即学习“Java免费学习笔记(深入)”;
- Lodash 的
_.debounce和_.throttle支持leading/trailing、maxWait等配置,且自带cancel和flush - React 用户建议用
useDebounce或useThrottle自定义 Hook,避免在组件重渲染时重复创建防抖函数 - 注意:Vue 3 的
watch本身不防抖,需配合lodash/debounce或async-validator类库手动包裹
真正容易被忽略的是「该用哪个」——不是看技术实现多优雅,而是看业务语义:用户输入后要不要等停顿?滚动过程中要不要及时反馈?这两个问题的答案,直接决定该选防抖还是节流,而不是反过来根据函数怎么写来凑场景。











