跳到主要内容

闭包与作用域

闭包(Closure)是函数式编程中的重要概念,它允许函数访问其词法作用域外的变量。

什么是闭包?

闭包是指有权访问另一个函数作用域中的变量的函数。即使外部函数已经执行完毕,闭包仍然可以访问外部函数的变量。

function outerFunction() {
let count = 0;

return function innerFunction() {
count++;
return count;
};
}

const counter = outerFunction();
counter(); // 1
counter(); // 2
counter(); // 3

闭包的工作原理

  1. 词法作用域:函数的作用域在定义时确定
  2. 变量持久化:闭包保持对外部变量的引用
  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!');

注意事项

  1. 内存管理:闭包会保持对外部变量的引用,可能导致内存泄漏
  2. 循环中的闭包:需要注意变量共享问题
// 问题代码
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 中强大而灵活的特性,它实现了:

  • 数据封装和私有变量
  • 函数工厂模式
  • 保持状态
  • 模块化代码