闭包与作用域
闭包(Closure)是函数式编程中的重要概念,它允许函数访问其词法作用域外的变量。
什么是闭包?
闭包是指有权访问另一个函数作用域中的变量的函数。即使外部函数已经执行完毕,闭包仍然可以访问外部函数的变量。
function outerFunction() {
let count = 0;
return function innerFunction() {
count++;
return count;
};
}
const counter = outerFunction();
counter(); // 1
counter(); // 2
counter(); // 3
闭包的工作原理
- 词法作用域:函数的作用域在定义时确定
- 变量持久化:闭包保持对外部变量的引用
- 数据封装:创建私有变量和方法
闭包的应用场景
1. 数据隐藏和封装
function createBankAccount(initialBalance) {
let balance = initialBalance;
return {
deposit: (amount) => {
balance += amount;
return balance;
},
withdraw: (amount) => {
if (amount <= balance) {
balance -= amount;
return balance;
}
return 'Insufficient funds';
},
getBalance: () => balance
};
}
const account = createBankAccount(100);
account.deposit(50); // 150
account.getBalance(); // 150
2. 函数工厂
function makeMultiplier(multiplier) {
return function(number) {
return number * multiplier;
};
}
const double = makeMultiplier(2);
const triple = makeMultiplier(3);
double(5); // 10
triple(5); // 15
3. 回调和异步操作
function setupButton(buttonId, message) {
const button = document.getElementById(buttonId);
button.addEventListener('click', function() {
alert(message); // 访问外部变量 message
});
}
setupButton('btn1', 'Hello!');
setupButton('btn2', 'World!');
注意事项
- 内存管理:闭包会保持对外部变量的引用,可能导致内存泄漏
- 循环中的闭包:需要注意变量共享问题
// 问题代码
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// 输出: 3, 3, 3
// 解决方案 1:使用 let
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// 输出: 0, 1, 2
// 解决方案 2:使用 IIFE
for (var i = 0; i < 3; i++) {
(function(j) {
setTimeout(() => console.log(j), 100);
})(i);
}
// 输出: 0, 1, 2
总结
闭包是 JavaScript 中强大而灵活的特性,它实现了:
- 数据封装和私有变量
- 函数工厂模式
- 保持状态
- 模块化代码