防抖和节流均依赖闭包保存定时器id或时间戳以控制函数执行频率:防抖通过清除重设定时器延迟执行最后一次调用,节流通过时间戳比较确保最小执行间隔;二者均可扩展立即执行、取消功能并需防范闭包内存泄漏。

防抖(Debounce)和节流(Throttle)是前端高频场景中控制函数执行频率的核心手段,它们都依赖闭包来保存定时器 ID 或时间戳等状态,从而实现“延迟执行”或“固定间隔执行”的逻辑。
防抖函数:用闭包保存 timer,延迟执行最后一次调用
防抖的思路是:在指定时间窗口内,如果函数被反复触发,就不断清除前一次的定时器,只执行最后一次触发后的回调。闭包的作用是让 timer 变量在多次调用间持续存在且不污染全局。
基础实现:
- 定义一个外层函数接收
fn和delay,返回一个新函数 - 在返回函数内部用
let timer = null声明定时器变量(闭包捕获) - 每次调用时先
clearTimeout(timer),再setTimeout新任务 - 注意 this 和参数传递:用
fn.apply(this, arguments)或展开语法保持上下文
示例代码:
function debounce(fn, delay) {let timer = null;
return function (...args) {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
}
节流函数:用闭包保存上一次执行时间,控制最小间隔
节流的目标是:确保函数至少间隔 interval 时间才执行一次。常见策略有“定时器版”和“时间戳版”,两者都靠闭包维持关键状态(如 lastTime 或 timer)。
时间戳版(推荐)更简单直接:
- 闭包保存
lastTime = 0 - 每次调用计算当前时间戳,与上次执行时间比较
- 若间隔超过
interval,则执行并更新lastTime - 否则不执行,等待下一次触发
示例代码:
function throttle(fn, interval) {let lastTime = 0;
return function (...args) {
const now = Date.now();
if (now - lastTime >= interval) {
fn.apply(this, args);
lastTime = now;
}
};
}
增强实用性:支持立即执行、取消和 this 绑定
实际项目中常需扩展功能,比如防抖支持“首次立即执行”,节流支持“取消 pending 状态”,这些都要靠闭包维护额外状态。
- 防抖加
immediate参数:首次调用立即执行,后续触发重置倒计时 - 节流加
leading/trailing控制首尾是否触发(需配合定时器版实现) - 暴露
cancel方法:闭包内保存 timer 或 lastTime,并返回带 cancel 的对象 - 避免 this 丢失:用
fn.call(context, ...args)显式绑定,或提前 bind
例如防抖 cancel 版本:
function debounce(fn, delay) {let timer = null;
const debounced = function (...args) {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
debounced.cancel = () => clearTimeout(timer);
return debounced;
}
使用注意事项:闭包不是万能,要防内存泄漏
闭包让状态长期存活,但也带来风险:
- 避免在闭包中引用大对象(如 DOM 节点、大型数组),否则 GC 无法回收
- 事件监听器中使用防抖/节流后,记得在组件卸载或元素销毁时调用
cancel - 不要在循环中无限制创建闭包函数(如给每个列表项单独 debounce),可复用同一实例
- 注意箭头函数中的
this是词法绑定,若需动态 this,用普通函数 + apply/call
不复杂但容易忽略。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











