Skip to content

防抖节流

在 JavaScript 中,节流(Throttle)防抖(Debounce) 是两种用于限制函数调用频率的技术。它们的主要作用是优化性能,防止某些操作(如滚动、调整窗口大小、输入事件等)频繁触发,导致性能问题。

一、防抖(Debounce)

1.什么是防抖?

防抖是一种延迟执行的技术。

它的原理是:

  • 当事件被触发时,延迟执行事件处理函数
  • 在延迟时间内如果事件再次被触发,则重新开始计时
  • 只有当事件停止触发达到设定的延迟时间后,目标函数才会真正执行。

2.适用场景

防抖主要适用于那些在用户停止操作后才需要执行的场景,例如:

  • 搜索输入框:用户在输入时,连续触发 keyup 事件,只有在输入结束后才发送请求。
  • 窗口调整:用户调整浏览器窗口大小时,频繁触发 resize 事件。防抖可以确保调整结束后再执行相应操作。
  • 表单验证:用户输入表单数据时,可以用防抖来减少频繁的验证请求。

3.防抖的实现

js
function debounce(func, delay) {
  //初始化定时器timer为null
  let timer = null;
  return function (...args) {
    clearTimeout(timer); // 清除之前的定时器
    timer = setTimeout(() => {
      func.apply(this, args); // 延迟执行函数
    }, delay);
  };
}
  • func:需要执行的目标函数。
  • delay:防抖的时间间隔(单位:毫秒)。
  • timer:用于存储定时器的 ID,初始为 null

这个返回的是一个闭包函数,它是防抖包装后的函数。

  • ...args:捕获传递给函数的参数。
  • context:存储当前上下文 this,以便在目标函数 func 内正确引用。

>[!tip] 本质 防抖的实现是在延迟 500ms 后执行目标函数,同时为目标函数绑定正确的上下文 this 和传入的参数

4.使用

js
// 模拟一个需要防抖的输入事件
function onInput(event) {
  console.log('Input value:', event.target.value);
}

const debounceOnInput = debounce(onInput, 500);

document.getElementById('search-input').addEventListener('input', debounceOnInput);

5.特性

  1. 执行次数减少:函数只有在事件触发后等待的时间内不再触发,才会被执行。
  2. 延迟执行:函数在等待时间结束后才会执行。

二、节流(Throttle)

1.什么是节流

节流是一种限制函数执行频率的技术。

它的原理是:

  • 当事件被频繁触发时,函数会按照一定的时间间隔执行,而不是每次触发事件都执行。换句话说,在一个时间段内,只会执行一次事件处理函数。

2.适用场景

节流适用于需要间隔时间执行的场景,例如:

  • 页面滚动:用户滚动页面时触发 scroll 事件,使用节流限制处理函数的执行频率。
  • 按钮点击:防止用户短时间内多次点击同一个按钮,造成重复提交。
  • 游戏动画:限制每秒渲染的帧数,以减少资源消耗。

3.节流的实现(时间戳版本)

js
function throttle(func, interval) {
  let lastTime = 0; // 上次执行的时间
  return function (...args) {
    const now = Date.now(); 
    if (now - lastTime >= interval) {
      func.apply(this, args); // 执行函数
      lastTime = now; // 更新执行时间
    }
  };
}

4.使用

js
// 模拟一个需要节流的滚动事件
function onScroll() {
  console.log('Scroll event triggered');
}

const throttleOnScroll = throttle(onScroll, 200);

window.addEventListener('scroll', throttleOnScroll);

5.特性

  1. 定时执行:无论事件触发次数有多频繁,函数都会按照设定的时间间隔执行。
  2. 立即执行:相比防抖,节流会更快触发第一次函数调用。

三、防抖与节流的区别

比较维度防抖(Debounce)节流(Throttle)
定义延迟执行,如果期间再次触发重新计时。限制执行频率,每隔一定时间执行一次。
触发时机最后一次触发事件后按照固定的时间间隔执行
适用场景输入框搜索、表单验证、窗口调整页面滚动、按钮点击、游戏动画
控制频率事件停止后执行一次固定时间间隔内执行一次
实现原理重新计时,如果时间内再次触发事件则清除计时器记录上次执行时间,或设置定时器

四、防抖和节流的进阶实现

防抖(Debounce)立即执行版本

有时需要在事件第一次触发时立即执行,而不是等待延迟结束后执行,可以在实现中添加一个选项控制:

js
function debounce(func, delay, immediate = false) {
  let timer = null;
  return function (...args) {
    const context = this;

    // 只会在 没有定时器timer=null 的情况下执行,防止在延迟期间重复触发。
    if (immediate && !timer) { 
      func.apply(context, args); // 立即执行
    }

    clearTimeout(timer);
    timer = setTimeout(() => {
      timer = null; // 延迟结束后重置
      if (!immediate) {  //防止反复执行两次func
        func.apply(context, args); // 延迟执行
      }
    }, delay);
  };
}
  • immediate:布尔值,是否立即执行目标函数。
  • timer = null:延迟结束后,将 timer 重置为 null,以便允许后续的立即执行(当 immediatetrue 时)。

用法:

js
const debouncedFunc = debounce(() => console.log('Action!'), 500, true);
document.addEventListener('mousemove', debouncedFunc);

节流(Throttle)定时器版本

除了时间戳实现,也可以通过定时器实现节流:

js
function throttle(func, interval) {
  let timer = null;
  return function (...args) {
    if (!timer) {
      timer = setTimeout(() => {
        func.apply(this, args);
        timer = null; // 定时器结束后重置
      }, interval);
    }
  };
}

用法:

js
const throttledFunc = throttle(() => console.log('Scroll triggered!'), 200);
window.addEventListener('scroll', throttledFunc);

总结

  • 防抖: 适合处理用户输入、搜索框请求等场景,减少函数调用次数。
  • 节流: 适合处理滚动、拖拽、窗口调整等场景,控制函数调用频率。

评论区

欢迎留言、补充或勘误。

xiaoba.blog