节流的核心逻辑是“固定时间窗口内最多执行一次”,保证函数在指定时间间隔内至少执行一次,典型用于scroll、mousemove事件;时间戳方案最简可靠,用Date.now()比对lastTime实现,避免setTimeout累积定时器导致的响应丢失。
节流(throttle)不是防抖(debounce),它不等待停止触发才执行,而是保证函数在指定时间间隔内**至少执行一次**。典型场景是 window.onscroll、mousemove 事件监听——你希望每 100ms 最多处理一次,而不是等用户停下才响应。
setTimeout + 时间戳实现最简可靠节流这是兼容性最好、逻辑最直白的写法,不依赖定时器叠加或闭包状态管理,避免了 setInterval 带来的同步风险和清理遗漏问题。
lastTime
lastTime 的差值delay),则立即执行,并更新 
lastTime;否则不执行function throttle(fn, delay) {
let lastTime = 0;
return function(...args) {
const now = Date.now();
if (now - lastTime >= delay) {
fn.apply(this, args);
lastTime = now;
}
};
}setTimeout + clearTimeout 模拟节流?这种写法常见但容易误用为防抖:它本质是“延迟执行并不断重置”,若用户持续高频触发,函数可能**永远不执行**——这违背节流“保底执行”的设计目标。
setTimeout 节流版本会累积定时器,需手动 clearTimeout,状态管理复杂this 和参数传递的完整性原生事件回调中的 this 指向触发元素(如 div),而节流函数返回的新函数默认丢失该绑定。必须用 fn.apply(this, args) 或 fn.call(this, ...args) 显式还原上下文。
...args 会导致事件对象(如 event)丢失,常见于 addEventListener 场景passive: true(否则 event 可能为只读或不可用)event 属性,因节流可能跨多次事件调用,状态易混乱节流看似简单,真正难的是在「不丢事件语义」和「不破性能底线」之间拿捏分寸——时间戳方案胜在确定性,而任何引入异步延迟的变体,都可能悄悄把“保底执行”变成“看运气执行”。