防抖和节流

50 阅读2分钟

防抖和节流是前端优化性能、控制事件触发频率的重要方法。以下是手写实现的代码和使用场景。

1. 防抖(Debounce)

防抖的核心思想是:在事件触发后,等待一段时间再执行回调,如果在等待时间内事件又被触发,则重新计时

实现代码
function debounce(fn, delay = 300) {
  let timer = null;
  return function (...args) {
    const context = this; // 保存 this 指向
    if (timer) clearTimeout(timer); // 如果有定时器,清除
    timer = setTimeout(() => {
      fn.apply(context, args); // 延迟后执行函数
    }, delay);
  };
}
使用示例

适用于搜索框输入窗口大小调整

const inputHandler = debounce(() => {
  console.log("执行搜索操作");
}, 500);

document.getElementById("searchInput").addEventListener("input", inputHandler);

2. 节流(Throttle)

节流的核心思想是:在一定时间内,只允许回调函数执行一次

实现代码(定时器版)
function throttle(fn, delay = 300) {
  let timer = null;
  return function (...args) {
    const context = this; // 保存 this 指向
    if (!timer) { // 如果没有定时器
      timer = setTimeout(() => {
        fn.apply(context, args); // 执行函数
        timer = null; // 清空定时器
      }, delay);
    }
  };
}
实现代码(时间戳版)
function throttle(fn, delay = 300) {
  let lastTime = 0;
  return function (...args) {
    const now = Date.now(); // 当前时间
    if (now - lastTime >= delay) {
      fn.apply(this, args); // 执行函数
      lastTime = now; // 更新上次执行时间
    }
  };
}
使用示例

适用于滚动事件按钮点击

const scrollHandler = throttle(() => {
  console.log("滚动事件触发");
}, 500);

window.addEventListener("scroll", scrollHandler);

3. 防抖和节流的区别

防抖(Debounce)

定义:事件触发后等待一段时间,若期间有新触发,则重新计时

触发频率:最终触发一次

使用场景:搜索输入、窗口调整等

节流(Throttle)

定义:一段时间内事件只会触发一次

触发频率:固定时间间隔触发一次

使用场景:滚动加载、按钮点击防止多次触发等

4. 合并版:支持立即执行的防抖

有时需要让防抖函数支持立即执行选项:

function debounce(fn, delay = 300, immediate = false) {
  let timer = null;
  return function (...args) {
    const context = this;
    if (timer) clearTimeout(timer);
    if (immediate && !timer) {
      // 立即执行一次
      fn.apply(context, args);
    }
    timer = setTimeout(() => {
      fn.apply(context, args);
    }, delay);
  };
}

5. 合并版:支持立即执行的节流

实现节流时,可以支持“头部触发”和“尾部触发”:

function throttle(fn, delay = 300, options = { leading: true, trailing: true }) {
  let timer = null;
  let lastTime = 0;

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

    if (!options.leading && !lastTime) {
      lastTime = now;
    }

    const remaining = delay - (now - lastTime);

    if (remaining <= 0 || remaining > delay) {
      if (timer) {
        clearTimeout(timer);
        timer = null;
      }
      fn.apply(context, args);
      lastTime = now;
    } else if (!timer && options.trailing) {
      timer = setTimeout(() => {
        fn.apply(context, args);
        timer = null;
        lastTime = options.leading ? Date.now() : 0;
      }, remaining);
    }
  };
}

6. 总结

  • 防抖(Debounce) :控制事件只在“空闲时”触发,适合频繁触发的场景。

  • 节流(Throttle) :控制事件在“固定间隔”内触发,适合需要平滑触发的场景。 根据实际需求,灵活选择或二次封装即可满足开发场景。