防抖(Debounce)与节流(Throttle)

4 阅读2分钟

防抖(Debounce)与节流(Throttle)

防抖(Debounce)

防抖函数指的是触发事件后,在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。

实现原理

function debounce(fn, delay) {
  let timer = null;

  return function (...args) {
    if (timer) {
      clearTimeout(timer);
    }

    timer = setTimeout(() => {
      fn.apply(this, args);
      timer = null;
    }, delay);
  };
}

使用示例

// 防抖应用场景:搜索框输入,窗口大小调整
const debouncedSearch = debounce(function (keyword) {
  console.log("搜索关键词:", keyword);
}, 300);

// 用户输入时调用
document.querySelector("#search").addEventListener("input", function (e) {
  debouncedSearch(e.target.value);
});

节流(Throttle)

节流函数指的是在一段时间内,函数只能执行一次,也就是说在规定的时间内,函数只能执行一次。

实现原理

function throttle(fn, interval) {
  let lastTime = 0;

  return function (...args) {
    const now = Date.now();

    if (now - lastTime >= interval) {
      fn.apply(this, args);
      lastTime = now;
    }
  };
}

使用示例

// 节流应用场景:滚动事件,鼠标移动,DOM元素拖拽
const throttledScroll = throttle(function () {
  console.log("页面滚动了");
}, 500);

// 滚动时调用
window.addEventListener("scroll", throttledScroll);

防抖与节流的区别

特性防抖(Debounce)节流(Throttle)
目的将多次执行变为最后一次执行将多次执行变成每隔一段时间执行一次
场景搜索框输入、窗口大小调整滚动事件、鼠标移动、DOM 元素拖拽
原理清除之前的定时器,重新设置新的定时器判断当前时间与上次执行的时间差

进阶版本实现

立即执行版本的防抖

function debounceImmediate(fn, delay, immediate = false) {
  let timer = null;

  return function (...args) {
    if (timer) clearTimeout(timer);

    // 立即执行版本
    if (immediate && !timer) {
      fn.apply(this, args);
    }

    timer = setTimeout(() => {
      if (!immediate) fn.apply(this, args);
      timer = null;
    }, delay);
  };
}

可取消的节流

function throttleWithCancel(fn, interval) {
  let lastTime = 0;
  let timer = null;

  const throttled = function (...args) {
    const now = Date.now();
    const remaining = interval - (now - lastTime);

    if (remaining <= 0) {
      if (timer) {
        clearTimeout(timer);
        timer = null;
      }
      fn.apply(this, args);
      lastTime = now;
    } else if (!timer) {
      timer = setTimeout(() => {
        fn.apply(this, args);
        lastTime = Date.now();
        timer = null;
      }, remaining);
    }
  };

  throttled.cancel = function () {
    if (timer) {
      clearTimeout(timer);
      timer = null;
    }
  };

  return throttled;
}