什么是防抖和节流?有什么区别?如何实现?

2024-07-21 17:56:19 133
**防抖(Debounce)和节流(Throttle)**是两种用于控制函数执行频率的技术,通常用于提高性能和用户体验。虽然它们的目标相似,但实现和应用场景有所不同。

防抖(Debounce)

概念:防抖是一种确保在一段时间内,函数只执行一次的技术。具体来说,当事件被触发时,会延迟执行函数。如果在延迟时间内再次触发事件,则重新开始计时。只有在延迟时间结束后没有再次触发事件,函数才会执行。

应用场景

  • 搜索框输入:用户停止输入后再进行搜索请求。
  • 窗口调整:用户停止调整窗口大小后再执行相关操作。

实现防抖

function debounce(fn, delay) {
  let timeoutId; // 用于存储定时器的ID

  return function(...args) {
    clearTimeout(timeoutId); // 清除前一个定时器
    timeoutId = setTimeout(() => {
      fn.apply(this, args); // 定时器到期后执行函数
    }, delay);
  };
}

// 示例:防抖搜索框
const searchInput = document.getElementById('search');
const handleSearch = debounce((event) => {
  console.log('Searching:', event.target.value);
}, 300); // 延迟300毫秒

searchInput.addEventListener('input', handleSearch);

节流(Throttle)

概念:节流是一种确保在一定时间内,函数最多执行一次的技术。具体来说,当事件被频繁触发时,在每个固定的时间段内,只允许函数执行一次。

应用场景

  • 滚动事件:限制滚动事件处理程序的执行频率。
  • 按钮点击:防止按钮被快速多次点击导致的多次提交。

实现节流

function throttle(fn, limit) {
  let lastCall = 0; // 记录上次函数执行的时间

  return function(...args) {
    const now = Date.now(); // 当前时间
    if (now - lastCall >= limit) { // 如果距离上次执行时间超过了限制时间
      lastCall = now; // 更新上次执行时间
      fn.apply(this, args); // 执行函数
    }
  };
}

// 示例:节流滚动事件
const handleScroll = throttle(() => {
  console.log('Scrolling');
}, 200); // 每200毫秒最多执行一次

window.addEventListener('scroll', handleScroll);

防抖和节流的区别

  1. 防抖

    • 在一段时间内,函数只执行一次。即事件停止触发后的延迟时间内再执行函数。
    • 适用于在用户停止操作后才进行处理的场景。
  2. 节流

    • 在每个固定的时间段内,函数最多执行一次。
    • 适用于限制函数执行频率的场景。

具体实现

以下是更详细的防抖和节流实现,包括立即执行和取消功能:

防抖(立即执行和取消)

function debounce(fn, delay, immediate) {
  let timeoutId; // 用于存储定时器的ID

  function debounced(...args) {
    const callNow = immediate && !timeoutId; // 是否立即执行
    clearTimeout(timeoutId); // 清除前一个定时器
    timeoutId = setTimeout(() => {
      timeoutId = null; // 定时器到期后清空timeoutId
      if (!immediate) fn.apply(this, args); // 如果不是立即执行,延迟时间到期后执行函数
    }, delay);
    if (callNow) fn.apply(this, args); // 立即执行函数
  }

  debounced.cancel = () => {
    clearTimeout(timeoutId); // 取消防抖
    timeoutId = null;
  };

  return debounced;
}

// 示例:立即执行防抖
const log = debounce(() => console.log('Hello'), 1000, true);
window.addEventListener('resize', log);

节流(立即执行和取消)

function throttle(fn, limit, immediate) {
  let lastCall = 0; // 记录上次函数执行的时间
  let timeoutId; // 用于存储定时器的ID

  function throttled(...args) {
    const now = Date.now(); // 当前时间
    if (immediate && !lastCall) { // 是否立即执行
      lastCall = now;
      fn.apply(this, args); // 立即执行函数
    }

    if (now - lastCall >= limit) { // 如果距离上次执行时间超过了限制时间
      clearTimeout(timeoutId); // 清除前一个定时器
      lastCall = now; // 更新上次执行时间
      fn.apply(this, args); // 执行函数
    } else if (!timeoutId) { // 如果没有定时器,则设置一个定时器
      timeoutId = setTimeout(() => {
        lastCall = immediate ? Date.now() : 0;
        timeoutId = null; // 定时器到期后清空timeoutId
        fn.apply(this, args); // 延迟时间到期后执行函数
      }, limit - (now - lastCall));
    }
  }

  throttled.cancel = () => {
    clearTimeout(timeoutId); // 取消节流
    lastCall = 0;
    timeoutId = null;
  };

  return throttled;
}

// 示例:立即执行节流
const log = throttle(() => console.log('Hello'), 1000, true);
window.addEventListener('scroll', log);

总结

  • 防抖(Debounce):在一段时间内确保函数只执行一次,适用于用户停止操作后再进行处理的场景。
  • 节流(Throttle):在每个固定的时间段内确保函数最多执行一次,适用于限制函数执行频率的场景。
  • 实现:使用闭包和定时器来实现防抖和节流,防抖可以立即执行并支持取消,节流可以控制执行频率并支持取消。