什么是闭包?
闭包(Closure)是JavaScript中一个让很多初学者头疼的概念。但说穿了,它其实很简单:一个函数能"记住"它被创建时所在作用域的环境。
想象你有一个箱子,里面装着几个变量。你把箱子封好,然后把这个箱子交给另一个人。即使这个人离开了你当初放箱子的房间,他打开箱子,依然能看到里面的东西。闭包就是这样一个"箱子"。
用代码来说:
function createCounter() {
let count = 0; // 这是外部变量
return function() {
count++;
return count;
};
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
createCounter 函数执行完了,按理说它的局部变量 count 应该被销毁。但返回的内层函数仍然能访问 count,这就是闭包。
闭包有什么用?
1. 数据私有化
JavaScript没有像Java那样的private关键字,但闭包可以模拟私有变量:
function createBankAccount(initialBalance) {
let balance = initialBalance; // 外部无法直接访问
return {
deposit(amount) {
balance += amount;
return balance;
},
withdraw(amount) {
if (amount <= balance) {
balance -= amount;
return balance;
}
return "余额不足";
},
getBalance() {
return balance;
}
};
}
const account = createBankAccount(1000);
console.log(account.getBalance()); // 1000
console.log(account.balance); // undefined(无法直接访问)
2. 函数工厂
根据参数动态生成不同的函数:
function makeMultiplier(factor) {
return function(num) {
return num * factor;
};
}
const double = makeMultiplier(2);
const triple = makeMultiplier(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15
3. 保存状态
最经典的例子就是计数器,或者分页、定时器管理等需要"记住"状态的场景。
闭包怎么产生?
记住三个条件:
- 函数嵌套:一个函数定义在另一个函数内部
- 内部函数引用了外部函数的变量
- 内部函数被返回到外部作用域使用
三个条件缺一不可。如果内部函数没有引用外部变量,就不会形成闭包。
常见误区
误区一:闭包就是匿名函数
不是。匿名函数只是语法形式,闭包是一种机制。命名函数同样可以形成闭包。
误区二:每次调用都创建新闭包
不对。只要内部函数引用了外部变量,就形成闭包。多次调用同一个函数,每次创建的是新的闭包实例,共享相同的代码但拥有独立的状态。
误区三:闭包很慢
早期浏览器确实有闭包性能问题,但现代JavaScript引擎已经优化得很好了。正常业务场景不需要担心。
总结
闭包的核心就一句话:函数带着它出生时的环境一起行动。
它让你能在JavaScript中实现数据封装、状态保持、函数工厂等强大功能。理解闭包,是理解JavaScript异步编程、模块化设计的基石。
下次看到"为什么这个变量还在"的时候,你就知道——是闭包在起作用。