Skip to content

防抖与节流 (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);
  };
}

总结

  1. 防抖:必须用 clearTimeout 阻止旧任务执行。
  2. 节流:必须用 timer=null 解除自定义锁,允许新任务进入。

口诀:timer 是你的“人工阀门”,需手动打开关闭,不能依赖 JS 自动收水龙头。

Released under the MIT License.