节流函数通过控制执行频率避免高频触发,时间戳法用 Date.now() 比较间隔实现基础节流,定时器法用 setTimeout 加锁确保尾部执行,现代写法支持 cancel 方法便于生命周期管理。

节流函数的核心是控制函数在指定时间间隔内最多执行一次,避免滚动事件高频触发导致性能问题。关键在于记录上一次执行时间,用时间差判断是否允许本次执行。
基础时间戳节流实现
利用 Date.now() 记录上次执行时间,每次触发时比较当前时间与上次执行时间的差值:
- 若时间差大于设定阈值(如 100ms),则立即执行并更新时间戳
- 否则不执行,等待下次触发
let lastTime = 0;
return function(...args) {
const now = Date.now();
if (now - lastTime >= delay) {
func.apply(this, args);
lastTime = now;
}
};
}
更健壮的定时器节流(带“尾部”执行)
时间戳方式可能丢掉最后一次快速滚动后的回调。用 setTimeout + 定时器锁 可兼顾节流与兜底执行:
- 首次触发立即执行,并启动延时定时器
- 后续触发若定时器未执行,则清除旧定时器、重设新定时器
- 确保在“静止”后仍有一次执行(适合需要最终状态的场景)
let timeoutId = null;
return function(...args) {
if (!timeoutId) {
func.apply(this, args);
timeoutId = setTimeout(() => {
timeoutId = null;
}, delay);
}
};
}
实际使用注意事项
绑定滚动事件时需注意上下文和清理,避免内存泄漏:
立即学习“Java免费学习笔记(深入)”;
- 使用 addEventListener 而非 onscroll 属性,便于后续移除
- 节流函数需用 bind 或箭头函数保持 this 指向(或直接用闭包传参)
- 页面卸载前调用 removeEventListener 清理监听器
- 移动端建议监听 touchmove 并配合 { passive: false }(仅当需 preventDefault 时)
推荐:现代写法 + 可取消设计
封装为可取消的节流函数,便于测试和生命周期管理:
function throttle(func, delay) {let timeoutId = null;
const throttled = function(...args) {
if (!timeoutId) {
func.apply(this, args);
timeoutId = setTimeout(() => {
timeoutId = null;
}, delay);
}
};
throttled.cancel = () => {
if (timeoutId) {
clearTimeout(timeoutId);
timeoutId = null;
}
};
return throttled;
}
这样可在组件销毁时主动调用 handler.cancel(),更安全可靠。


















