品鉴JS的魅力之防抖与节流【JS】

JavaScript 中的防抖(Debounce)和节流(Throttle)技术。它们是处理高频事件(如滚动、输入、调整窗口大小、鼠标移动等)的两种重要优化手段,能有效提升应用性能和用户体验。


核心思想与区别

特性防抖 (Debounce)节流 (Throttle)
核心思想“回城被打断”“技能冷却”
形象比喻电梯门:等人进完等一会儿再关门,如果中间又有人来,则重新计时。游戏技能:释放一次后,必须等待冷却时间结束才能再次释放。
作用将多次高频操作合并为最后一次执行。确保在一定时间间隔内只执行一次操作。
适用场景搜索框输入联想、窗口 resize 结束后的操作。滚动加载、鼠标移动、按钮频繁点击。

1. 防抖 (Debounce)

防抖函数会在一连串事件触发后,等待指定的时间间隔(例如 wait 毫秒)。如果在这个等待期内没有新的事件触发,那么就执行函数。如果等待期内又有新事件触发,则取消之前的计时,并重新开始计时

  • 简单实现:
function debounce(func, wait) {
  let timeoutId;
  // 返回一个闭包函数,利用闭包保存定时器
  return function (...args) {
    // 清除上一次的定时器
    clearTimeout(timeoutId);
    // 设置新的定时器
    timeoutId = setTimeout(() => {
      func.apply(this, args); // 使用 apply 确保正确的作用域和参数
    }, wait);
  };
}
  • 使用示例:搜索框联想
<input type="text" id="search-input" />

<script>
  function fetchSuggestions(query) {
    console.log(`正在搜索: ${query}`);
    // 这里应该是实际的 AJAX 请求
  }

  const debouncedFetch = debounce(fetchSuggestions, 500);

  document.getElementById('search-input').addEventListener('input', (e) => {
    debouncedFetch(e.target.value); // 只有在用户停止输入500ms后才会执行
  });
</script>
  • 带立即执行选项的防抖 (Immediate Debounce)
    有时我们希望事件第一次触发时立即执行函数,然后等到停止触发一段时间后才可以重新触发。
function debounce(func, wait, immediate) {
  let timeoutId;
  return function (...args) {
    const callNow = immediate && !timeoutId; // 如果是立即执行模式且当前没有计时
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => {
      timeoutId = null; // 重置定时器ID
      if (!immediate) { // 如果不是立即执行模式,则执行函数
        func.apply(this, args);
      }
    }, wait);
    if (callNow) {
      func.apply(this, args);
    }
  };
}

2. 节流 (Throttle)

节流函数会确保一个函数在指定的时间间隔内最多只执行一次。即使事件持续频繁地触发,也会按照固定的频率执行。

实现方式主要有两种:使用定时器 或 使用时间戳。

方法一:使用定时器 (Timer)
function throttle(func, wait) {
  let timeoutId;
  return function (...args) {
    if (!timeoutId) { // 如果定时器不存在,则可以设置
      timeoutId = setTimeout(() => {
        timeoutId = null; // 执行完毕后清空定时器
        func.apply(this, args);
      }, wait);
    }
  };
}
方法二:使用时间戳 (Timestamp) - 立即执行
function throttle(func, wait) {
  let lastTime = 0;
  return function (...args) {
    const now = Date.now();
    if (now - lastTime >= wait) { // 如果距离上次执行已经过了wait时间
      lastTime = now;
      func.apply(this, args);
    }
  };
}
  • 两种方法的区别:

    • 时间戳版:事件第一次会立即触发,停止触发后不会再额外执行一次。
    • 定时器版:事件第一次会延迟 wait ms 后触发,停止触发后还会再执行一次(因为最后一個定时器还会触发)。
  • 结合版(常用): 兼具第一次立即执行和停止后最后执行一次的特性。

function throttle(func, wait) {
  let timeoutId;
  let lastTime = 0;
  return function (...args) {
    const now = Date.now();
    const remaining = wait - (now - lastTime); // 计算还剩多久可以执行

    if (remaining <= 0) {
      // 如果剩余时间<=0,说明可以立即执行
      if (timeoutId) {
        clearTimeout(timeoutId);
        timeoutId = null;
      }
      lastTime = now;
      func.apply(this, args);
    } else if (!timeoutId) {
      // 如果剩余时间>0,且没有设置定时器,则设置一个定时器在剩余时间后执行
      timeoutId = setTimeout(() => {
        lastTime = Date.now(); // 更新最后执行时间
        timeoutId = null;
        func.apply(this, args);
      }, remaining);
    }
  };
}
  • 使用示例:无限滚动加载
function checkScrollAndLoad() {
  console.log('检查视口位置,加载更多数据...');
  // 计算距离底部还有多少像素,如果小于阈值,则加载下一页
}

const throttledScrollHandler = throttle(checkScrollAndLoad, 200);

window.addEventListener('scroll', throttledScrollHandler);
// 即使用户疯狂滚动,每200ms也只会检查一次是否需要加载

总结与选择

场景推荐技术原因
搜索框输入联想防抖用户输入过程中不需要请求,只在输入停顿后请求最后一次输入的内容。
窗口大小调整 (resize)防抖调整结束后再计算布局,避免调整过程中频繁进行昂贵的 DOM 计算。
按钮防止重复提交防抖节流防抖(立即执行):第一次点击立即提交,之后一段时间内点击无效。节流:固定时间内只允许提交一次。
无限滚动加载节流在用户滚动时,按固定频率检查位置,而不是每次 scroll 事件都检查。
鼠标移动事件 (mousemove)节流需要实时跟随但不需要极高的精度(如拖拽),用节流降低执行频率。
动画通常使用 requestAnimationFrame这是浏览器为动画提供的专有节流API,比 setTimeout 更高效。
  • 简单记忆:
    • 防抖:关心最终状态(结果是什么)。
    • 节流:关心过程状态(以固定的频率了解状态)。

在实际项目中,你可以根据具体需求选择最合适的技术,或者使用 Lodash 等库中已经高度优化过的 _.debounce_.throttle 函数。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蓝莓味的口香糖

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值