�������� JavaScript �հ�����ԭ����ʵս

深入理解 JavaScript 闭包:从原理到实战

闭包是 JavaScript 中最核心也最容易让人困惑的概念之一。本文通过通俗的语言和实际例子,帮你彻底搞懂它。

什么是闭包?

闭包(Closure) 是指一个函数能够访问其外部作用域中变量的能力------即使这个外部函数已经执行完毕。

简单来说:函数 + 它所记住的外部变量 = 闭包

一个最简单的例子

javascript 复制代码
function makeCounter() {
  let count = 0;
  return function() {
    count++;
    return count;
  };
}

const counter = makeCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3

makeCounter 执行完后,count 变量并没有消失。内部的匿名函数"记住"了它所在的作用域,每次调用都能访问并修改 count,这就是闭包。

闭包的核心原理:作用域链

JavaScript 采用词法作用域(Lexical Scoping),函数的作用域在定义时就确定了,而不是调用时。每个函数在创建时都会保存一个对其外部作用域的引用,这个引用就是闭包的本质。

常见使用场景

1. 数据私有化(模拟私有变量)

javascript 复制代码
function createAccount(initialBalance) {
  let balance = initialBalance; // 外部无法直接访问

  return {
    deposit(amount) {
      balance += amount;
    },
    withdraw(amount) {
      if (amount <= balance) balance -= amount;
      else console.log('余额不足');
    },
    getBalance() {
      return balance;
    }
  };
}

const account = createAccount(1000);
account.deposit(500);
console.log(account.getBalance()); // 1500
console.log(account.balance); // undefined,无法直接访问

2. 函数工厂(批量创建相似函数)

javascript 复制代码
function multiplier(factor) {
  return (number) => number * factor;
}

const double = multiplier(2);
const triple = multiplier(3);

console.log(double(5));  // 10
console.log(triple(5));  // 15

3. 防抖与节流

javascript 复制代码
// 防抖:用户停止输入 300ms 后再执行
function debounce(fn, delay) {
  let timer;
  return function(...args) {
    clearTimeout(timer);
    timer = setTimeout(() => fn.apply(this, args), delay);
  };
}

const handleInput = debounce((e) => {
  console.log('搜索:', e.target.value);
}, 300);

document.querySelector('#search').addEventListener('input', handleInput);

4. 模块模式(封装功能)

javascript 复制代码
const TodoList = (() => {
  const todos = [];
  
  return {
    add(item) { todos.push(item); },
    remove(item) {
      const index = todos.indexOf(item);
      if (index > -1) todos.splice(index, 1);
    },
    getAll() { return [...todos]; }
  };
})();

TodoList.add('学习闭包');
TodoList.add('写一篇博客');
console.log(TodoList.getAll()); // ['学习闭包', '写一篇博客']

经典陷阱:循环中的闭包

错误写法(for + var)

javascript 复制代码
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
}
// 输出:3 3 3(不是期望的 0 1 2)

原因:var 声明的变量共享同一个 i,循环结束后 i 为 3。

正确写法 1:使用 let(推荐)

javascript 复制代码
for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
}
// 输出:0 1 2

正确写法 2:使用立即执行函数(IIFE)

javascript 复制代码
for (var i = 0; i < 3; i++) {
  ((j) => {
    setTimeout(() => console.log(j), 100);
  })(i);
}
// 输出:0 1 2

注意:闭包与内存

闭包会持有外部作用域变量的引用,只要闭包存在,这些变量就不会被垃圾回收。如果大量创建闭包且不及时释放,可能导致内存泄漏。

合理使用,用完后将引用置为 null 即可:

javascript 复制代码
let fn = makeCounter();
// 使用...
fn = null; // 释放引用,GC 可以回收了

总结

闭包是 JavaScript 中"函数是一等公民"理念的完美体现。理解了闭包,你就掌握了 JS 中许多高级模式的底层原理------从 React Hooks 到 Promises,背后都有闭包的影子。

你有什么关于闭包的疑问或有趣案例?欢迎在评论区交流!

相关推荐
尽兴-20 小时前
4.1 智能体核心:Agent、Sub-Agent、ReAct、规划执行
前端·javascript·react.js·agent·react·subagent
万物更新_20 小时前
vue框架
前端·javascript·vue.js·笔记
Richar20 小时前
Object.freeze()注意事项
前端·javascript
TA远方20 小时前
【HTML】JavaScript Canvas 图像截取与保存完整指南
前端·javascript·html·canvas·截图·截取
Asize20 小时前
JavaScript 数据类型解析:从 null 与 undefined 的迷思到栈堆内存真相
前端·javascript·面试
LDX前端校草21 小时前
position属性值及用法
前端·javascript·面试
晓131321 小时前
【Cocos Creator 3.x】篇——第四章 子系统
前端·javascript·游戏引擎
li-xun21 小时前
我的在线工具箱继续升级:新增 Token 计算器、AI 提示词生成器和开发者格式化工具
javascript·django·html5
ikoala21 小时前
Codex 怎么买、怎么充值?先把这两套计费搞清楚
前端·javascript·后端
前端Hardy1 天前
一个时代结束了:npm 终于对 install 脚本下手了
前端·javascript·后端