JavaScript 闭包
理解闭包——函数记住外部变量的能力 · 难度:高级 · +15XP
JavaScript 闭包深入理解
闭包(Closure)是 JavaScript 最核心的概念之一。当一个函数能够"记住"并访问其词法作用域(lexical scope)中的变量,即使该函数在其词法作用域之外被调用时,就形成了闭包。闭包是函数式编程的基础,广泛用于数据封装、模块化、回调函数、柯里化等场景。
闭包的形成条件
- 函数嵌套:内部函数引用外部函数的变量
- 内部函数被返回到外部作用域或被传递给其他函数
- 外部函数执行完毕后,内部函数仍持有对外部变量的引用
// 经典闭包示例
function createCounter() {
let count = 0; // 被闭包"捕获"的变量
return function() {
count++; // 访问外部作用域的变量
return count;
};
}
const counter1 = createCounter();
console.log(counter1()); // 1
console.log(counter1()); // 2
console.log(counter1()); // 3
const counter2 = createCounter();
console.log(counter2()); // 1(独立的闭包,有自己独立的 count)
// count 变量被"封闭"在返回的函数中
// 外部无法直接访问,但函数可以持续访问和修改
console.log(typeof count); // "undefined"(外部不可见)
闭包的实战应用
| 应用场景 | 说明 | 示例 |
|---|---|---|
| 数据封装/私有变量 | 隐藏内部状态 | 计数器、缓存 |
| 函数工厂 | 生成特定行为的函数 | 乘法器、格式化工具 |
| 模块模式 | 封装方法和私有数据 | IIFE + 闭包 |
| 事件处理 | 保持对循环变量的引用 | for 循环 + 闭包 |
// 应用1: 函数工厂
function multiplyBy(factor) {
return function(number) {
return number * factor;
};
}
const double = multiplyBy(2);
const triple = multiplyBy(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15
// 应用2: 模块模式(利用闭包实现私有变量)
const User = (function() {
let privateId = 0;
return {
create(name) {
privateId++;
return { id: privateId, name };
},
getTotal() {
return privateId;
}
};
})();
const u1 = User.create("张三");
const u2 = User.create("李四");
console.log(u1.id); // 1
console.log(User.getTotal()); // 2
console.log(typeof privateId); // "undefined"(外部不可访问)
// 应用3: 解决经典的 for 循环闭包陷阱
for (var i = 1; i <= 3; i++) {
setTimeout((function(j) {
return function() { console.log(j); };
})(i), i * 1000);
}
// 输出: 1, 2, 3(而不是 4, 4, 4)
// 现代写法:用 let 代替 var 即可