节流函数必须依赖闭包管理状态变量,lasttime和timer需在闭包顶层声明并严格控制更新与清理时机,raf版还需isqueued与缓存数据协同,多实例靠闭包天然隔离。

节流函数必须依赖闭包来管理状态变量,否则无法在多次事件触发间保持一致性。关键不在“用不用闭包”,而在“怎么用”——状态变量的声明位置、更新时机、清理逻辑,直接决定节流是否可靠、可复用、不泄漏。
lastTime 必须在闭包顶层声明并只读更新
时间戳版节流靠比较当前时间和上一次执行时间来决策。这个 lastTime 不能在每次调用时重置,也不能暴露给外部修改。
- 它得定义在外层函数作用域内,被返回的节流函数持续引用
- 仅在真正执行回调后才更新:
lastTime = Date.now() - 不能在判断前就赋值,否则会导致间隔失效;也不能在判断外赋值,否则可能漏更
timer 需统一持有、及时清除、执行后归零
定时器版依赖 setTimeout 延迟执行,但高频触发下旧定时器必须被取消,否则会堆积或错乱执行。
- timer 变量同样放在闭包中,初始为
null或undefined - 每次调用先检查是否有待执行的 timer,有则
clearTimeout(timer) - 新定时器设好后必须赋值给闭包变量,且回调执行完毕要手动置为
null - 不置空会导致后续调用误判,尤其在组件卸载后仍残留 timer 引用
RAF 场景用 isQueued + 缓存数据双状态协同
基于 requestAnimationFrame 的节流更贴近渲染节奏,但它不靠时间差,而靠帧调度标志和数据缓存。
- isQueued 是布尔开关,确保同一帧最多注册一次 RAF
- 滚动位置、视口尺寸等动态值需在节流函数入口处捕获并暂存(如
scrollTop = window.pageYOffset) - RAF 回调里用缓存值做计算,执行完立刻重置
isQueued = false - 两个状态缺一不可:只管排队不管数据,会用到过期值;只缓存不控队列,仍会重复注册
多实例隔离靠闭包天然作用域边界
同一个页面多个滚动区域、多个搜索框,各自需要独立节流逻辑。闭包让每个节流函数拥有专属状态空间。
- 每次调用
throttle(fn, delay)都新建一个闭包,lastTime或timer互不干扰 - 避免用全局对象属性模拟状态(如
window.throttleState),易覆盖、难调试 - 也不推荐用
this绑定存储,因事件回调的this不稳定,且无法保证唯一性











