节流函数的核心是控制执行时机,关键在闭包变量更新时机:lastTime需在真正执行后更新以确保时间窗口有效;timer须严格同步定时器生命周期;闭包保障状态持久性;leading/trailing模式要求变量更新节奏精准匹配执行逻辑。

节流函数的核心在于控制函数执行频率,关键不是“能不能执行”,而是“什么时候执行”。闭包中变量的更新时机直接决定节流行为是否符合预期——比如是“首次触发立即执行、后续按间隔执行”,还是“首次触发后延迟执行、之后再间隔执行”。变量(如 lastTime 或 timer)的赋值位置,决定了节流策略的语义和边界行为。
lastTime 更新位置决定“时间窗口”起点
以时间戳节流为例,通常用 lastTime 记录上一次成功执行的时间。它的更新必须发生在函数**真正执行之后**,否则会导致节流失效:
- 如果在判断前就更新
lastTime,相当于每次调用都重置窗口,失去节流意义; - 如果只在执行分支里更新(即
if (now - lastTime >= delay)为真时),才能保证只有实际执行的那次才推进时间窗口; - 常见错误是把
lastTime = now写在 if 外部或条件判断之前,这会让连续高频调用始终满足条件。
timer 变量需在定时器启动和清除时同步更新
定时器节流依赖 timer 标识来管理延迟任务。这个变量必须严格跟随定时器生命周期变化:
- 启动定时器时,要赋值
timer = setTimeout(...),确保下次调用能检测到已有待执行任务; - 在定时器回调中执行完业务逻辑后,必须手动置为
null或undefined,否则后续调用会误判“已有定时器”而跳过新任务; - 若使用
clearTimeout(timer)但未清空timer变量,下次调用仍可能因timer非 null 而不启动新定时器,造成漏执行。
闭包变量作用域保障状态持久性
节流函数返回的是一个新函数,所有状态变量(lastTime、timer、delay 等)都定义在外部函数作用域中,被内部函数闭包引用:
- 每次调用节流工厂函数(如
throttle(fn, 100))都会创建独立的闭包环境,互不影响; - 变量不会被外部干扰,也不随调用次数重置,天然满足“跨多次调用维持状态”的需求;
- 注意不要在节流函数内部重新声明同名变量(如
let timer),否则会覆盖闭包引用,导致状态丢失。
leading 和 trailing 模式影响变量更新节奏
带首尾执行选项的节流,变量更新逻辑更精细:
-
leading: true 时,首次调用立即执行,此时应立刻更新
lastTime(或启动定时器并记录开始时间); -
trailing: true 时,最后一次“没赶上窗口”的调用需延迟执行,这意味着
timer必须在每次调用时尝试设置(即使有旧 timer,也要 clearTimeout 后重设),且仅在定时器回调中更新lastTime; - 两种模式同时开启时,需避免重复执行:通常让 leading 执行后禁用 trailing 的延迟,或用标志位控制,这时变量(如
isInvoking)的更新顺序直接影响是否漏掉某次调用。

















