防抖和节流均依赖闭包保存timer或lastTime状态以控制执行频率:防抖清除前次定时器并延后执行,适用于输入搜索等场景;节流通过时间戳或定时器限制固定间隔内最多执行一次,适用于滚动、鼠标移动等高频操作。

在 JavaScript 中,防抖(Debounce)和节流(Throttle)都常借助闭包来保存定时器引用或时间戳等状态,从而实现对函数执行频率的控制。闭包让内部函数能持续访问外部作用域中的变量(比如 timer 或 lastTime),这是它们正常工作的关键。
防抖:只响应最后一次触发
防抖适用于“等用户操作停下了再执行”,比如搜索框输入、窗口大小调整。核心逻辑是每次触发时清除上一次的定时器,重新设置新的延迟执行。
- 闭包保存
timer变量,避免全局污染,也保证多次调用共享同一计时上下文 - 使用
setTimeout延迟执行,每次新调用先clearTimeout(timer) - 支持立即执行模式(可选),即首次触发立刻运行,后续连续触发则重置等待
示例:
function debounce(fn, delay, immediate = false) {let timer;
return function(...args) {
if (immediate && !timer) {
fn.apply(this, args);
}
clearTimeout(timer);
timer = setTimeout(() => {
if (!immediate) {
fn.apply(this, args);
}
timer = null;
}, delay);
};
}
节流:固定间隔内最多执行一次
节流适合高频但需稳定节奏的场景,如鼠标移动、滚动监听。关键在于记录上一次执行时间,判断是否达到间隔阈值。
立即学习“Java免费学习笔记(深入)”;
- 闭包维持
lastTime(时间戳)或timer(定时器),用于状态记忆 - 时间戳版:每次执行前比对
Date.now() - lastTime >= delay,满足才运行并更新时间 - 定时器版:用
setTimeout控制下一次可执行时机,期间忽略新调用
时间戳版示例:
function throttle(fn, delay) {let lastTime = 0;
return function(...args) {
const now = Date.now();
if (now - lastTime >= delay) {
fn.apply(this, args);
lastTime = now;
}
};
}
注意 this 和参数的正确传递
闭包中返回的函数会丢失原始调用上下文,所以要用 fn.apply(this, args) 或 fn.call(this, ...args) 保证 this 和参数准确传递。
- 不能直接写
fn(...args),否则this指向会变成undefined(严格模式)或全局对象 - 使用剩余参数
...args和展开语法,兼容任意数量的实参 - 若需绑定固定
this,可在封装前用fn.bind(context)预处理
实际使用时的清理与销毁
尤其在组件卸载或事件解绑时,未清除的定时器会造成内存泄漏。
- 防抖函数可暴露一个
cancel方法,用于手动清除 pending 的定时器 - 节流若用定时器实现,也建议提供类似清理机制
- React 等框架中,在
useEffect的清理函数里调用cancel()
例如为防抖添加 cancel:
const debounced = debounce(handleSearch, 300);// ...
debounced.cancel = () => clearTimeout(timer);


















