JavaScript中防抖和节流通过闭包保存timer或lastTime等状态,实现频率控制:防抖只执行最后一次触发,节流保证固定间隔最多执行一次;均需正确处理this、参数及内存泄漏。

JavaScript 中通过闭包实现防抖(debounce)和节流(throttle)函数,核心在于利用闭包保存定时器引用或时间戳等状态,避免每次调用都重新初始化,从而控制函数执行频率。
防抖函数:只响应最后一次触发
防抖适用于搜索框输入、窗口大小调整等场景——用户连续操作时,只在停止触发后执行一次。闭包用来保存上一次设置的定时器 ID,每次新触发时先清除旧定时器,再设新定时器。
- 定义一个外层函数接收原函数 fn 和延迟时间 delay
- 返回一个内层函数,该函数每次执行时先 clearTimeout 上次的 timer(靠闭包记住)
- 再用 setTimeout 设置新的延时执行
- 注意 this 和参数传递:用 apply 或展开运算符保持上下文和参数正确
function debounce(fn, delay) {
let timer = null;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delay);
};
}节流函数:固定间隔最多执行一次
节流适合鼠标移动、滚动监听等高频事件——保证函数至少间隔一定时间才执行一次。闭包用于保存上次执行的时间戳或锁定状态。
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
- 可以用“时间戳”方式:记录上次执行时间,当前时间减去它,大于 delay 才执行并更新时间戳
- 也可以用“定时器”方式:用闭包存 timer,只有 timer 为空时才启动新定时器,执行完再清空
- 推荐时间戳方式,更精准;定时器方式执行时机略滞后但逻辑更直观
function throttle(fn, delay) {
let lastTime = 0;
return function(...args) {
const now = Date.now();
if (now - lastTime >= delay) {
fn.apply(this, args);
lastTime = now;
}
};
}增强实用性:支持立即执行与取消
实际项目中常需支持首次立即执行(leading),或手动取消待执行任务(cancel)。这些能力都依赖闭包维持的状态。
立即学习“Java免费学习笔记(深入)”;
- 加个 immediate 参数,为 true 时第一次调用立刻执行,后续按 delay 限制
- 给返回的函数挂载 cancel 方法,清除定时器或重置时间戳
- cancel 需访问闭包内的 timer / lastTime,所以必须在闭包作用域内定义
function debounce(fn, delay) {
let timer = null;
const debounced = function(...args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delay);
};
debounced.cancel = () => {
clearTimeout(timer);
timer = null;
};
return debounced;
}注意事项:this、参数和内存管理
闭包让函数能“记住”外部变量,但也带来潜在问题,需主动处理:
- 直接写 fn(...args) 会丢失 this,务必用 fn.apply(this, args) 或 fn.call(this, ...args)
- 箭头函数不能用作内层函数,因为它不绑定自己的 this,会导致上下文错误
- 长期存在的防抖/节流函数若未清理,timer 引用可能阻碍垃圾回收,暴露 cancel 方法有助于手动释放
- 在 React 等框架中,组件卸载后仍触发回调会造成内存泄漏,应配合 useEffect cleanup 使用 cancel

















