防抖的核心是只执行最后一次触发的回调,闭包使timer变量私有持久、不被重复声明或清空;每次调用都清除旧定时器并设置新定时器,确保仅最后一次有效。

防抖的核心是:在事件频繁触发时,只执行最后一次触发后的回调。闭包的作用是让 timer 变量在多次调用中保持私有且持久,不被重复声明或清空。
闭包保存 timer 引用
每次调用防抖函数返回的包装函数时,它都能访问外层函数作用域中的 timer 变量。这个变量不会随调用结束而销毁,而是被内部函数持续“记住”。
关键点:
-
timer声明在外层函数(如debounce)中,只初始化一次 - 返回的函数通过闭包捕获并操作同一个
timer引用 - 每次触发都先清除旧定时器,再设新定时器,确保只有最后一次有效
基础实现代码示例
以下是一个简洁、可复用的防抖函数:
function debounce(fn, delay) {
let timer = null;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
}
使用方式:
const handleInput = debounce((value) => {
console.log('发送请求:', value);
}, 300);
inputElement.addEventListener('input', (e) => {
handleInput(e.target.value);
});
支持立即执行与取消的增强版
实际项目中常需支持“首次立即执行”和手动取消。这时仍依赖闭包维持 timer 和新增的控制状态:
function debounce(fn, delay, immediate = false) {
let timer = null;
const debounced = function(...args) {
const callNow = immediate && !timer;
clearTimeout(timer);
timer = setTimeout(() => {
timer = null;
if (!immediate) {
fn.apply(this, args);
}
}, delay);
if (callNow) {
fn.apply(this, args);
}
};
debounced.cancel = function() {
clearTimeout(timer);
timer = null;
};
return debounced;
}
这样 debounced.cancel() 就能外部中断等待中的执行,而 timer 依然靠闭包隔离维护。
为什么必须用闭包?不用会怎样?
如果把 timer 放在全局或每次调用都重新声明:
- 全局变量:污染作用域,多个防抖实例互相干扰
- 局部变量(无闭包):每次调用都新建
timer,无法清除前一次的定时器,导致多次执行
只有闭包能让每个防抖实例拥有自己独立、持久、不可见的 timer 状态。










