闭包(Closures)
闭包是 JavaScript 中一个基础且强大的概念。当一个函数能够记住并访问其所在的词法作用域(lexical scope)时,即使该函数在其词法作用域之外执行,闭包就产生了。
简单来说,闭包 = 函数 + 该函数被创建时所访问的自由变量。
一个简单的闭包示例
javascript
function createGreeter(greeting) {
// `greeting` 是一个自由变量
return function(name) {
// 这个内部函数是一个闭包
console.log(`${greeting}, ${name}!`);
};
}
// createGreeter 执行完毕后,其作用域本应销毁
// 但闭包的存在,使得 greeting 变量被保留了下来
const sayHello = createGreeter("Hello");
const sayHi = createGreeter("Hi");
sayHello("Alice"); // 输出: Hello, Alice!
sayHi("Bob"); // 输出: Hi, Bob!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
在上面的例子中,createGreeter
返回了一个内部函数。这个内部函数“记住”了它被创建时的环境,特别是 greeting
变量。因此,即使 createGreeter
已经执行完毕,sayHello
和 sayHi
仍然可以访问 greeting
。
常见用例
1. 创建私有变量
闭包是 JavaScript 中实现私有变量和方法的常用方式,这被称为模块模式(Module Pattern)。
javascript
function createPlayer(name) {
let health = 100; // 这是一个“私有”变量
function takeDamage(amount) {
health -= amount;
if (health < 0) health = 0;
console.log(`${name}受到了${amount}点伤害,剩余生命:${health}`);
}
function getHealth() {
return health;
}
// 返回一个对象,只暴露公共接口
return {
name: name,
dealDamage: takeDamage,
checkHealth: getHealth
};
}
const player = createPlayer("Gandalf");
player.dealDamage(20); // Gandalf受到了20点伤害,剩余生命:80
console.log(player.checkHealth()); // 80
// 无法直接访问 health 变量
console.log(player.health); // undefined
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2. 在循环中捕获变量
在使用 var
的 for
循环中,闭包可以解决一个常见的问题:循环结束后,所有回调函数都引用了同一个变量。
javascript
// 错误示例
for (var i = 1; i <= 3; i++) {
setTimeout(function() {
console.log(i); // 会输出三次 4
}, i * 100);
}
// 正确示例 (使用闭包)
for (var i = 1; i <= 3; i++) {
(function(j) { // 使用立即执行函数表达式(IIFE)创建一个新的作用域
setTimeout(function() {
console.log(j); // 输出 1, 2, 3
}, j * 100);
})(i);
}
// ES6+ 更简单的方法
// `let` 会为每次循环创建一个新的块级作用域
for (let i = 1; i <= 3; i++) {
setTimeout(function() {
console.log(i); // 输出 1, 2, 3
}, i * 100);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
闭包是理解许多高级 JavaScript 模式和框架的基础,值得花时间去掌握。