🚀 防抖节流:前端摸鱼指南

83 阅读4分钟

🧩 闭包的七十二变

🎁 记忆函数 - 让计算像快递一样准时

image.png

💡 核心概念
闭包(Closure,别闭嘴!)就像一个带私房钱的保姆,她能记住你家的存款密码(外部变量),即使你换锁了(函数执行完毕)也能随时取钱。

// 记忆函数:像快递柜存包裹
function memoize(fn) {
  const cache = {}; // 保姆的私房钱
  return function(...args) {
    const key = JSON.stringify(args);
    if (cache[key]) return cache[key]; // 直接取快递
    return (cache[key] = fn.apply(this, args)); // 存新快递
  };
}

const add = memoize((a, b) => a + b);
console.log(add(1, 2)); // 第一次计算
console.log(add(1, 2)); // 直接取缓存 🚀

文字流程图

用户调用 -> 检查快递柜(cache) -> 有包裹就直接取 📦
           -> 没包裹就下单计算 🛒
           -> 把结果存进快递柜 📥

常见误区预警
⚠️ 别踩这个坑!如果你用普通对象存储缓存,可能会被GC回收导致"私房钱被偷"。使用闭包就能永久保存记忆。

image.png


🧱 类封装 - 隐藏家务的魔法盒子

image.png

💡 核心概念
类就像带密码的保险箱,对外只展示取款机(public API),私有财产(private variables)只能通过内部方法操作。

// 类封装:家庭账本管理系统
class FamilyAccount {
  #balance = 0; // 私有财产(用#标记)
  
  deposit(amount) {
    this.#balance += amount; // 正常收入
  }
  
  secretExpense(amount) {
    this.#balance -= amount; // 私密支出
  }
  
  showBalance() {
    return this.#balance;
  }
}

const account = new FamilyAccount();
account.deposit(1000); // 正常存款
account.secretExpense(500); // 隐藏支出 💸
console.log(account.showBalance()); // 500

文字流程图

用户调用公开方法 -> 操作私有属性 -> 返回结果
               -> 不能直接访问私有属性(#balance)🔒

性能对比

  • 普通对象:属性直接暴露 → 安全风险 🔥
  • 类封装:属性私有化 → 提升代码安全性 ✅

⚡️ 防抖节流大作战

🤖 防抖:快递员的等待游戏

image.png

💡 核心概念
防抖就像快递员等电梯:用户频繁下单(输入搜索词)→ 快递员(AJAX请求)会一直等待,直到5分钟内没新订单才出发送货。

// 防抖:快递员等电梯
function debounce(fn, delay) {
  let timer = null;
  return function(...args) {
    clearTimeout(timer); // 取消之前的等待
    timer = setTimeout(() => {
      fn.apply(this, args); // 最终执行
    }, delay);
  };
}

// 应用场景:实时搜索建议
const inputBox = document.getElementById('searchInput');
const debouncedSearch = debounce((query) => {
  console.log(`搜索对乡乡的爱在哪个地方❤️: ${query}`); // 模拟AJAX请求
}, 300);

inputBox.addEventListener('input', (e) => {
  debouncedSearch(e.target.value);
});

文字流程图

用户输入 → 清除旧计时器 ⏳ → 设置新计时器
         → 如果继续输入 → 重置计时器
         → 停止输入后 → 执行最终请求 📦

性能对比

  • 未优化:输入10个字符 → 10次请求 🚨
  • 防抖优化:输入10个字符 → 1次请求 ✅

🛠️ 节流:打工人的时间管理大师

image.png

💡 核心概念
节流就像打工人摸鱼:每小时必须完成3项任务(固定频率),无论领导(用户操作)多么频繁催促。

// 节流:打工人摸鱼
function throttle(fn, delay) {
  let lastCall = 0;
  return function(...args) {
    const now = Date.now();
    if (now - lastCall >= delay) {
      fn.apply(this, args); // 执行任务
      lastCall = now;
    }
  };
}

// 应用场景:滚动事件优化
window.addEventListener('scroll', throttle(() => {
  console.log('页面滚动中...我对乡乡的爱一直都在...'); // 模拟懒加载
}, 1000));

文字流程图

用户操作 → 检查上次执行时间 🕒
         → 如果间隔足够 → 执行任务 ✅
         → 如果间隔不够 → 跳过本次操作 🙈

性能对比

  • 未优化:滚动100次 → 100次执行 🥵
  • 节流优化:滚动100次 → 约10次执行 ✅

🧪 代码实战实验室

🧪1. 防抖demo - 带反悔机制的购物车

image.png

// 购物车价格计算(防抖+反悔)
function calculateTotalPrice(items) {
  // 模拟耗时计算
  let total = 0;
  items.forEach(item => total += item.price * item.count);
  console.log(`总价: ¥${total}`); // 最终结算
}

const cart = {
  items: [],
  add(item) {
    this.items.push(item);
    // 防抖计算
    debounce(() => calculateTotalPrice(this.items), 500)();
  }
};

// 用户疯狂加购
cart.add({ price: 10, count: 2 }); // 过早计算会被取消 😅
cart.add({ price: 20, count: 1 }); // 最终计算 ✅

执行流程动画

用户添加商品 → 启动倒计时 ⏳
             → 继续添加商品 → 重置倒计时
             → 停止添加后 → 最终结算 💰

🧪2. 节流demo - 摸鱼时的完美滑动

image.png

// 懒加载图片(节流优化)
function lazyLoadImages() {
  const images = document.querySelectorAll('img[data-src]');
  images.forEach(img => {
    if (isInViewport(img)) {
      img.src = img.dataset.src;
      img.removeAttribute('data-src');
    }
  });
}

// 节流控制加载频率
window.addEventListener('scroll', throttle(lazyLoadImages, 300));

// 辅助函数:判断元素是否在视口内
function isInViewport(el) {
  const rect = el.getBoundingClientRect();
  return (
    rect.top >= 0 &&
    rect.left >= 0 &&
    rect.bottom <= (window.innerHeight || document.documentElement.clientHeight)
  );
}

性能对比

  • 未优化:滚动1000px → 50次加载 🤯
  • 节流优化:滚动1000px → 约4次加载 ✅

❓FAQ

image.png

Q1: 防抖和节流到底有什么区别?
A: 防抖是「最后一次生效」(快递员等电梯),节流是「每隔一段时间生效」(打工人摸鱼)。就像外卖配送 vs 定时送餐 🍔

Q2: 闭包真的有必要学吗?
A: 闭包是JavaScript的灵魂!它让你能:

  • 创建私有变量(家庭保险箱)🔐
  • 实现记忆函数(快递柜存包裹)📦
  • 封装复杂逻辑(黑盒操作)⚙️

Q3: this指向问题怎么解决?
A: 三板斧:

  1. 箭头函数(自动绑定)➡️
  2. bind方法(强制绑定)📌
  3. that=this(经典土办法)👴

📚 小结

image.png

通过本次学习,我们掌握了:

  1. 闭包的七十二变:从记忆函数到私有变量,闭包就像JavaScript的瑞士军刀
  2. 防抖节流大作战:学会用快递员等电梯和打工人摸鱼的类比理解这两个高频优化技巧
  3. 类封装的艺术:用家庭账本管理系统案例理解面向对象编程的核心思想

记住:前端开发就像打游戏,遇到性能瓶颈不要慌,拿出防抖节流的大招,优雅地解决卡顿问题!🎮

技术写作就像写情书,既要专业严谨,又要让人看得懂 ❤️
—— 一位摸鱼的前端工程师