节流函数采用时间戳+定时器双重判断:时间戳确保首次触发立即执行且间隔≥delay,定时器兜底保证最后一次触发不被丢弃,协同实现“首次立即、后续节流、尾部兜底”。

节流函数(Throttle)的核心目标是控制函数执行频率,避免高频触发(如 scroll、resize)导致性能问题。时间戳 + 定时器双重判断是一种经典且鲁棒的实现方式:它既利用时间戳保证「至少间隔 delay 才执行一次」,又借助定时器兜底确保「最后一次触发后仍能执行」,兼顾立即性与完整性。
为什么需要双重判断?
单用时间戳方案(只比对上次执行时间)会导致最后一次触发被丢弃;单用定时器方案(每次触发都重设 setTimeout)则可能在持续高频触发时完全不执行——二者各有缺陷。双重判断结合两者优势:
- 时间戳用于「节流窗口内首次触发立即执行」
- 定时器用于「窗口内后续触发暂存,窗口结束时补发最后一次」
- 两者协同,实现「首次立即 + 后续节流 + 尾部兜底」
核心逻辑拆解
关键在于维护两个状态:上一次执行的时间戳(previous)和正在运行的定时器(timer)。每次调用时:
- 先检查距上次执行是否已超
delay:满足则立即执行,并更新previous - 否则,若无活跃定时器,则启动一个延迟为
delay - (now - previous)的定时器(或统一设为delay,更简洁) - 定时器回调中执行函数,并清空
timer,同时更新previous(可选,取决于是否允许「定时器执行后立刻再触发」)
标准实现代码(含注释)
};
}function throttle(func, delay) {
let previous = 0; // 上次执行时间戳
let timer = null; // 当前定时器引用
<p>return function(...args) {
const now = Date.now();</p><pre class="brush:php;toolbar:false;">// ✅ 时间戳判断:已过 delay,立即执行
if (now - previous >= delay) {
func.apply(this, args);
previous = now;
}
// ❌ 未到 delay,启用定时器兜底
else if (!timer) {
timer = setTimeout(() => {
func.apply(this, args);
previous = Date.now(); // 更新时间戳,避免连续触发堆积
timer = null;
}, delay - (now - previous)); // 精确等待剩余时间,或直接 delay 更稳妥
}
使用建议与注意事项
- 推荐将定时器延迟统一设为
delay,而非动态计算剩余时间,避免因系统调度误差导致提前/延后 - 若需支持取消节流(如组件卸载),返回的函数应暴露
cancel方法来清除定时器 - 注意
this和参数透传,使用func.apply(this, args)或箭头函数 + 展开运算符保持上下文 - 实际项目中可配合 leading/trailing 配置(是否立即执行首调 / 是否保证尾调),但双重判断本身已隐含 trailing 行为
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











