🚀 JS 最常用的性能优化 防抖和节流

🚀 JS 最常用的性能优化 防抖和节流

先看个问题:

  • 你在搜索框打字,每打一个字就发一次请求 → 1 秒打 5 个字,发 5 次请求 → 服务器压力大,页面卡顿
  • 你滚动页面,每滚动 1px 就执行一次代码 → 1 秒滚动 100px,执行 100 次 → 浏览器累死,页面掉帧

核心痛点 :有些事件(inputscrollresizemousemove)触发频率极高,如果每次触发都执行代码,会严重浪费性能,导致页面卡顿。

  • 防抖 :"等你停下来,我再执行" 大部分面向后端请求
  • 节流 :"每隔一段时间,只执行一次" 大部分面向前端渲染

防抖

事件触发后,等待一段时间 (比如 300ms),如果这段时间内没有再次触发 ,才执行代码;如果又触发了 ,就重新计时

js 复制代码
/**
 * 防抖函数
 * @param {Function} fn - 要执行的函数
 * @param {Number} delay - 等待时间(毫秒)
 * @returns {Function} - 防抖后的函数
 */
function debounce(fn, delay) {
  let timer = null; // 存定时器的变量

  // 返回一个新函数
  return function(...args) {
    // 如果之前有定时器,先清除(重新计时)
    if (timer) clearTimeout(timer);

    // 重新设置定时器,等待 delay 毫秒后执行 fn
    timer = setTimeout(() => {
      fn.apply(this, args); // 用 apply 保证 this 指向正确,args 是参数
    }, delay);
  };
}

真实场景:搜索框实时搜索

html 复制代码
<input type="text" id="searchInput" placeholder="输入搜索内容...">

<script>
  // 模拟搜索请求
  function search(keyword) {
    console.log('发送搜索请求:', keyword);
  }

  // 用防抖包装搜索函数:停止输入 300ms 后才发请求
  const debouncedSearch = debounce(search, 300);

  const input = document.getElementById('searchInput');
  input.addEventListener('input', function(e) {
    debouncedSearch(e.target.value); // 调用防抖后的函数
  });

  // 防抖函数
  function debounce(fn, delay) {
    let timer = null;
    return function(...args) {
      if (timer) clearTimeout(timer);
      timer = setTimeout(() => {
        fn.apply(this, args);
      }, delay);
    };
  }
</script>

节流

事件持续触发时,每隔固定时间 (比如 200ms),只执行一次代码,不管中间触发了多少次。

js 复制代码
/**
 * 节流函数(时间戳版)
 * @param {Function} fn - 要执行的函数
 * @param {Number} interval - 间隔时间(毫秒)
 * @returns {Function} - 节流后的函数
 */
function throttle(fn, interval) {
  let lastTime = 0; // 记录上次执行的时间

  return function(...args) {
    const nowTime = Date.now(); // 当前时间

    // 如果当前时间 - 上次执行时间 >= 间隔时间,才执行
    if (nowTime - lastTime >= interval) {
      fn.apply(this, args);
      lastTime = nowTime; // 更新上次执行时间为当前时间
    }
  };
}

// 定时器
function throttle(fn, interval) {
  let timer = null;

  return function(...args) {
    // 如果没有定时器,才设置
    if (!timer) {
      timer = setTimeout(() => {
        fn.apply(this, args);
        timer = null; // 执行完后清空定时器,允许下次执行
      }, interval);
    }
  };
}

真实场景:滚动页面显示位置

html 复制代码
<div style="height: 2000px;">向下滚动试试...</div>
<div id="scrollPos" style="position: fixed; top: 10px; left: 10px;"></div>

<script>
  // 显示滚动位置
  function showPos() {
    const pos = window.scrollY;
    document.getElementById('scrollPos').innerText = `滚动位置:${pos}px`;
  }

  // 用节流包装:每隔 200ms 只执行一次
  const throttledShowPos = throttle(showPos, 200);

  window.addEventListener('scroll', throttledShowPos);

  // 节流函数
  function throttle(fn, interval) {
    let lastTime = 0;
    return function(...args) {
      const nowTime = Date.now();
      if (nowTime - lastTime >= interval) {
        fn.apply(this, args);
        lastTime = nowTime;
      }
    };
  }
</script>
相关推荐
Captaincc11 小时前
来自 Codex 官方团队的分享:如何把 Codex 用到极致
前端·vibecoding
做人求其滴11 小时前
面试经典 150 题 380 274
c++·算法·面试·职场和发展·力扣
小江的记录本11 小时前
【Java基础】Java 8-21新特性:JDK21 LTS:虚拟线程、模式匹配switch、结构化并发、序列集合(附《思维导图》+《面试高频考点清单》)
java·数据库·python·mysql·spring·面试·maven
lichenyang45312 小时前
鸿蒙聊天 Demo 练习 05:新增登录功能,实现登录态保存与页面访问控制
前端
还有多久拿退休金12 小时前
我用 Three.js 造了个 3D 漫步世界,角色走路像喝醉了——以及我是怎么修好的
前端·vue.js
SZLSDH12 小时前
场景适配论 | 数字孪生IOC建设中渲染技术与智能体能力的协同逻辑
前端·数据库·ai·数字孪生·数据可视化·智能体
_按键伤人_12 小时前
二、从零搭建本地 RAG 知识库
前端·llm·ai编程
_按键伤人_12 小时前
一、理解 RAG:从概念到实践
前端·llm·ai编程
lichenyang45312 小时前
鸿蒙聊天 Demo 练习 04:聊天历史本地缓存,实现消息记录持久化
前端
名字都不重要何况昵称12 小时前
canvas 元素拾取
前端·canvas