### 1. 核心概念 (Core Concept)

闭包（Closure）是 JavaScript 中一个核心概念，它是指一个函数以及其周围状态（lexical environment，词法环境）的引用打包在一起的组合。换句话说，闭包允许我们将函数与该函数所运行的外部环境隔离开来。

### 2. 为什么需要它？ (The "Why")

1.  **数据封装和私有变量 (Data Encapsulation and Private Variables):** 闭包提供了一种机制来创建私有作用域，使得某些变量只能在特定的函数内部访问，从而实现数据的封装，避免全局污染或意外修改。
2.  **保持状态 (Maintaining State):** 闭包可以“记住”创建它们时的环境，即使外部函数已经执行完毕，闭包内部仍能访问到外部函数作用域中的变量，从而在函数调用之间保持状态。
3.  **延迟执行和事件处理 (Deferred Execution and Event Handling):** 在异步操作、事件处理或定时器中，常常需要函数在 future 的某个时间点执行，闭包可以确保这些函数在执行时能访问到当时正确的作用域变量。

### 3. API 与用法 (API & Usage)

闭包并非一个需要显式调用或特殊语法的 API，它是 JavaScript 语言中基于词法作用域的一种自然产物。当一个内部函数被“暴露”到其词法作用域之外时，闭包就形成了。

**判断闭包的核心依据是：** 一个函数是否能够记住并访问到在其**词法作用域**外部定义的变量，即使该外部作用域的函数已经执行完毕。

以下是一个经典的闭包示例：

```javascript
function makeGreeter(greeting) {
  // 'greeting' 是 makeGreeter 的局部变量
  // 返回的匿名函数形成了一个闭包
  return function(name) {
    console.log(greeting + ', ' + name + '!');
  };
}

// 当 makeGreeter() 执行完毕后，它的作用域理论上应该销毁
const sayHello = makeGreeter('Hello');
const sayHey = makeGreeter('Hey');

// 但是，sayHello 和 sayHey 函数（闭包）
// 仍然能够访问到创建它们时 makeGreeter 的 'greeting' 变量
sayHello('Alice'); // 输出: Hello, Alice!
sayHey('Bob');   // 输出: Hey, Bob!
```

在这个例子中，`sayHello` 和 `sayHey` 是由 `makeGreeter` 返回的内部函数。即使 `makeGreeter` 已经执行完成（其调用栈已弹出），但 `sayHello` 和 `sayHey` 仍然可以访问到 `makeGreeter` 作用域内的 `greeting` 变量。这就是闭包的体现。

另一个常见的例子，用于解决循环中的异步问题：

```javascript
// 错误示例：i 在定时器执行时已经是 5
// for (var i = 0; i < 5; i++) {
//   setTimeout(function() {
//     console.log(i); // 总是输出 5
//   }, i * 100);
// }

// 使用闭包解决 (以函数参数创建新作用域)
for (var i = 0; i < 5; i++) {
  (function(index) {
    setTimeout(function() {
      console.log(index); // 输出 0, 1, 2, 3, 4
    }, index * 100);
  })(i); // 将当前的 i 值作为参数传递给立即执行函数 (IIFE)
}

// 使用 let (块级作用域，自然形成闭包)
for (let j = 0; j < 5; j++) {
  setTimeout(function() {
    console.log(j); // 输出 0, 1, 2, 3, 4
  }, j * 100);
}
```
在这个循环示例中，通过立即执行函数（IIFE）创建新的作用域，将当前的 `i` 值“捕获”进去，从而使得定时器回调函数（闭包）能够访问到每次迭代时正确的 `index` 值。使用 `let` 声明则利用了其块级作用域的特性，每轮循环都会为 `j` 创建一个新的绑定，从而自然地解决了问题，这也体现了块级作用域与闭包的关系。

### 4. 关键注意事项 (Key Considerations)

1.  **内存开销 (Memory Overhead):** 闭包会使其引用的外部变量一直保存在内存中，直到闭包本身被垃圾回收。如果闭包不再需要，应解除对它的引用，避免潜在的内存泄漏。
2.  **“捕获”变量的引用 (Capturing Variable Reference):** 闭包捕获的是外部变量的引用，而不是值的副本（基本类型在多数情况下看起来像副本是因为它们是按值传递的，但闭包记住的是变量本身）。如果外部变量的值在其被捕获后发生改变，闭包内部访问到的将是改变后的值（除非使用技巧如 IIFE 或 `let` 在每次迭代中创建新的变量绑定）。
3.  **判断的核心是词法作用域 (Judgment Based on Lexical Scope):** 闭包的形成与函数的定义位置（词法作用域）相关，而不是执行位置。判断一个函数是否形成闭包，是看该函数是否访问了其定义位置所在的外部作用域的变量。
4.  **JavaScript 中的常见现象 (Common Phenomenon in JS):** 闭包在 JavaScript 中非常常见，几乎所有的回调函数、事件处理器、模块模式等等都广泛使用了闭包的特性。理解闭包对于理解 JavaScript 的运行机制至关重要。

### 5. 参考资料 (References)

*   [MDN Web Docs: Closures](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures)
*   [JavaScript.info: Closures](https://javascript.info/closure)
*   [You Don't Know JS Yet: Scope & Closures](https://github.com/getify/You-Dont-Know-JS/blob/2nd-ed/scope-closures/README.md) (推荐深度阅读)