防抖函数的核心是只执行最后一次触发的回调:利用闭包持久保存timer变量,每次触发先clearTimeout旧定时器再setTimeout新定时器,确保仅最后一次有效。

防抖函数的核心是:在事件频繁触发时,只执行最后一次的回调。实现的关键在于用闭包保存上一次设置的定时器 ID(timer),每次新触发时先清除旧定时器,再设新的。
基础防抖函数实现(带立即执行选项)
下面是一个常见、可复用的防抖函数写法:
<!-- JavaScript -->
function debounce(func, wait, immediate = false) {
let timer = null;
return function(...args) {
const later = () => {
timer = null;
if (!immediate) func.apply(this, args);
};
const callNow = immediate && !timer;
clearTimeout(timer);
timer = setTimeout(later, wait);
if (callNow) func.apply(this, args);
};
}闭包为何能持久保存 timer?
返回的匿名函数形成了闭包,它“记住”了外层函数作用域中的 timer 变量。即使 debounce 执行完毕,这个变量仍被内部函数持有,不会被垃圾回收。
- 每次调用防抖后的函数,都访问的是同一个
timer引用(不是副本) -
clearTimeout(timer)能准确清除上一次未执行的定时器 - 若不用闭包(比如把
timer放全局或参数传入),就无法保证多次调用间的状态隔离
为什么必须每次 clearTimeout 再 setTimeout?
这是防抖行为的逻辑基础:
- 用户连续输入/滚动时,每次都重置倒计时,相当于“重新等待
wait毫秒” - 只有当两次触发间隔 >
wait,setTimeout才真正执行回调 - 不清理旧定时器会导致多个定时器并发运行,最终可能执行多次——那就变成“节流失败”甚至“乱执行”
实际使用注意点
- 绑定事件时,需用
debounce(fn, 300)的返回值,而不是直接传fn - 如果需要访问
this或事件对象,确保在防抖函数内正确传递(上面示例用了...args和apply) - 想手动清除定时器(比如组件卸载时),可暴露
cancel方法(扩展版常见做法)

















