防抖是将连续多次触发压缩为最后一次触发后的延迟执行,解决高频操作导致的性能问题;核心是每次触发重置定时器,停止触发超时后才执行函数。

防抖是什么?它解决什么问题
防抖(debounce)不是节流,也不是简单的延时执行——它是把「连续多次触发」压缩成「最后一次触发后的延迟执行」。典型场景是用户在 input 框中快速打字、resize 窗口、scroll 滚动时发起搜索或重绘,若不控制,可能一秒触发几十次函数,造成卡顿或多余请求。
核心逻辑:每次触发都重置定时器;只有停止触发超过指定时间后,才真正执行目标函数。
手写一个可靠防抖函数的要点
不能只用 setTimeout + clearTimeout 就完事。常见坑包括:this 指向丢失、参数无法透传、返回值被忽略、未提供取消能力。
- 必须用
func.apply(context, args)保留原始调用上下文和参数 - 返回一个可取消的函数,暴露
cancel方法(比如用户中途离开页面要清资源) - 支持立即执行模式(leading edge),即首次触发立刻执行,后续触发仍按防抖规则处理(需额外布尔参数)
- 避免闭包内变量污染:每次调用防抖函数应生成独立的定时器引用
function debounce(func, wait, immediate = false) {
let timeout = null;
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);
};
}
// 使用示例
const handleInput = debounce((e) => {
console.log('搜索:', e.target.value);
}, 300);
input.addEventListener('input', handleInput);
防抖 vs 节流:什么时候该选哪个
防抖适合「等用户停下来再响应」的场景,比如搜索建议、表单校验、窗口尺寸适配;节流(throttle)适合「保证固定频率执行」的场景,比如拖拽位置上报、Canvas 绘图帧率控制。
立即学习“Java免费学习笔记(深入)”;
- 防抖:高频触发 → 全部忽略,只响应最后一次停顿后的行为
- 节流:高频触发 → 每隔
wait毫秒最多执行一次,不管中间触发多少次 - 误用防抖处理滚动监听可能导致视觉反馈延迟明显;误用节流处理搜索输入会导致用户还没输完就发了请求
- 现代框架(如 React)中,防抖常配合
useCallback+ 依赖数组使用,避免每次渲染都新建防抖函数
实际项目中容易被忽略的细节
防抖不是加个包装函数就万事大吉。真实环境里几个关键点常被跳过:
- 事件监听器没移除 → 防抖函数内部的
timeout可能成为内存泄漏源,尤其在组件卸载时需手动cancel() - 服务端接口有防重机制,但前端防抖没对齐超时时间,导致用户快速连点两次,第二次请求仍被发出(比如防抖设了 500ms,但按钮点击间隔 400ms)
- 移动端
input在某些 Android 浏览器中会延迟触发,单纯防抖可能漏掉首次输入,需结合compositionstart/compositionend处理中文输入法 - 防抖时间不能硬编码:网络较慢时可设为 600ms,本地开发可设为 200ms,最好抽成配置项或根据响应时间动态调整
最麻烦的不是写不对,而是忘了它只管「执行时机」,不管「执行结果」——比如防抖后的请求失败了,得靠单独的错误重试逻辑兜底,这和防抖本身无关。











