防抖与节流必须用闭包维持状态变量(如timer、lasttime),否则每次调用都会新建独立作用域,导致无法清除旧定时器或判断时间间隔;闭包使内层函数持续持有外层变量引用,保障状态唯一可更新。

防抖与节流在 JavaScript 中依赖闭包来维持状态变量,核心在于:外层函数定义变量(如 timer、lastTime 或 flag),内层函数反复调用时仍能读写这些变量——因为闭包让变量“活”在内存中,不随外层函数执行结束而销毁。
为什么必须用闭包维护状态
高频事件(如 input、scroll、resize)触发时,每次回调都应共享同一份控制状态。若把 timer 声明在函数内部(非闭包方式),每次调用都会新建一个,无法清除前一次定时器,导致逻辑失效。
- 没有闭包 → 每次调用都是独立作用域 → 状态不延续 → 防抖变“每次都执行”,节流变“每次都能进”
- 有闭包 → 返回的函数持续持有对外层变量的引用 →
timer、lastTime等始终唯一且可更新
防抖中闭包维护 timer 的典型结构
防抖需在新触发时清除旧定时器、重设新定时器,timer 必须跨多次调用保持有效:
function debounce(fn, delay) {
let timer = null; // ← 外层变量,被闭包捕获
return function (...args) {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
timer = null; // 执行后清空,为下一次准备
}, delay);
};
}
每次调用返回的函数,都操作同一个 timer,这就是闭包在起作用。
节流中闭包维护时间戳或开关状态
节流有两种主流实现方式,都靠闭包保存关键状态:
-
时间戳方式:用
lastTime记录上一次执行时间,每次比对当前时间差 -
开关方式:用
flag标记是否可执行,执行后置为false,定时器结束后再置回true
例如时间戳版节流:
function throttle(fn, delay) {
let lastTime = 0;
return function (...args) {
const now = Date.now();
if (now - lastTime >= delay) {
fn.apply(this, args);
lastTime = now;
}
};
}
这里的 lastTime 被闭包长期持有,确保每次调用都能准确判断间隔是否达标。
注意 this 和参数的正确传递
闭包本身不改变 this 指向,返回的函数执行时 this 由调用方式决定(比如绑定在 DOM 元素上,this 就是该元素)。为保障业务函数拿到正确的上下文和参数:
- 用
fn.apply(this, args)或fn.call(this, ...args)显式绑定 - 避免直接写
fn(),否则this会丢失或指向window(非严格模式) - 使用 rest 参数
...args收集全部实参,兼容任意数量的传入参数
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











