防抖函数利用闭包保存 timer 变量,确保多次调用共享同一定时器引用;每次触发先清除旧定时器再设置新定时器,从而只执行最后一次回调,且不同实例互不干扰。

防抖函数利用闭包保存上一次设置的定时器 ID,从而在下一次触发时清除旧定时器、重设新定时器,避免重复执行。
闭包如何捕获并维护定时器状态
闭包让内部函数能持续访问外层函数定义的变量。防抖函数中,timerId 通常声明在外层函数作用域内,返回的事件处理函数通过闭包引用它,每次调用都能读取和更新这个值。
- 外层函数执行一次,生成独立的 timerId 变量空间
- 返回的处理函数每次运行时,先 clearTimeout(timerId) 清除上次未执行的定时器
- 再用 setTimeout 设置新定时器,并把新 ID 赋给 timerId
典型实现中闭包的关键位置
核心在于 return function 内部对 timerId 的读写——这个变量既不暴露给外部,又不会被重复初始化,正是闭包提供的“私有状态”。
- timerId 不能放在全局或函数参数里,否则无法跨次调用保持状态
- 也不能在每次事件触发时 let timerId 重新声明,那样就清不掉之前的定时器
- 必须在 debounce 工厂函数顶层用 let 或 var 声明,供内部函数反复使用
注意 clearTimeout 的安全性
即使 timerId 是 null 或 undefined,clearTimeout 也不会报错,所以无需额外判断,直接调用即可。
- 首次触发时 timerId 是 undefined,clearTimeout 无副作用
- 后续触发时,它保存的是上一轮 setTimeout 返回的数字 ID
- 定时器执行后,timerId 不会自动清空,需手动赋值(如设为 null)或依赖下次覆盖
带立即执行选项的扩展写法
如果支持立即执行(leading)+ 延迟执行(trailing),闭包还需维护额外状态,比如 isInvoking 标记是否已在本轮触发过,或用 lastCall 记录时间戳,这些变量同样靠闭包封装,不污染外部环境。
- leading 模式需要在首次进入时立刻执行,之后进入冷却期
- trailing 模式依赖最后一次触发后的延迟执行,仍需 timerId 控制时机
- 两种模式共存时,闭包内可能包含多个状态变量,但都由同一作用域统一管理

















