防抖与节流 (Debounce & Throttle)
代码实现
javascript
// 简化版防抖函数(基础实现)
function simpleDebounce(fn, delay) {
let timer = null;
return function (...args) {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
}
// 节流
function simpleThrottle(fn, delay) {
let timer = null;
return function (...arg) {
if (timer) return;
fn.apply(this, arg);
timer = setTimeout(() => {
timer = null;
}, delay);
};
}
// 使用示例
function myFn(name) {
console.log("执行函数,参数:", name, "时间:", new Date().toLocaleTimeString());
}
// 防抖示例
const myFnDebounce = simpleDebounce(myFn, 1000);
myFnDebounce("myFnDebounce 1");
myFnDebounce("myFnDebounce 2");
// 节流示例
const myFnThrottle = simpleThrottle(myFn, 1000);
myFnThrottle("myFnThrottle 1");
setTimeout(() => {
myFnThrottle("myFnThrottle 2");
}, 100);疑惑:为什么防抖要用 clearTimeout 而节流要用 timer=null?
核心区别对比
| 场景 | 防抖(Debounce) | 节流(Throttle) |
|---|---|---|
| 核心逻辑 | 动态更新定时器 → 需要 clearTimeout 来终止旧任务 | 静态互斥锁 → 需要主动解锁允许下次执行 |
| 锁定方式 | 依赖不断清除重置定时器(高动态性) | 依赖单次锁机制(低动态性) |
结论
timer=null 的唯一作用是解除自定义锁机制,而非真正的清除资源。真正的定时器资源由浏览器/JS 引擎自动回收。
追问:节流中都用 clearTimeout 可以吗?
不可以。
如果在节流中只写 clearTimeout(timer) 而不写 timer = null,会导致“死锁”。 因为 setTimeout 返回的是一个数字 ID,clearTimeout 只是取消了定时器任务,但不会把 timer 变量自动变成 null。 下一次执行时,if (timer) 依然为 true(因为 timer 还是那个数字),导致后续操作永远被阻挡。
javascript
// ❌ 错误示范:导致函数只能执行一次
function wrongThrottle(fn, delay) {
let timer = null;
return function () {
if (timer) return; // 第二次调用时,timer 还是之前的数字 ID,被拦截
fn();
timer = setTimeout(() => {
clearTimeout(timer); // 只是取消任务,timer 变量的值没变!
}, delay);
};
}总结
- 防抖:必须用
clearTimeout阻止旧任务执行。 - 节流:必须用
timer=null解除自定义锁,允许新任务进入。
口诀:timer 是你的“人工阀门”,需手动打开关闭,不能依赖 JS 自动收水龙头。
