闭包是JavaScript中函数捕获并持久化其词法环境的机制:内部函数引用外部变量时,该变量被保留在内存中,即使外部函数执行完毕。典型应用包括封装私有计数器、解决循环中异步索引共享问题、实现配置化工厂函数等,但需警惕内存泄漏。

闭包是 JavaScript 中保存局部作用域状态的核心机制:函数在定义时捕获并记住其词法环境,即使外部函数已执行完毕,内部函数仍能访问该环境中的变量。
用闭包封装私有计数器
最典型的例子是创建一个独立、可复用的计数器,每次调用都维持自己的状态,互不干扰:
function createCounter() {
let count = 0; // 局部变量,被闭包“捕获”
return function() {
count++;
return count;
};
}
const counter1 = createCounter();
const counter2 = createCounter();
console.log(counter1()); // 1
console.log(counter1()); // 2
console.log(counter2()); // 1 —— 独立状态
这里 count 是 createCounter 的局部变量,返回的匿名函数形成了闭包,持续持有对它的引用。
避免循环中变量共享问题
在 for 循环中直接为异步操作(如 setTimeout)绑定索引时,常因变量提升和作用域共享导致全部输出相同值。闭包可隔离每次迭代的状态:
使用 JSON Schema 验证 JSON 数据,从示例 JSON 生成 schema,并将其转换为 TypeScript 接口、Python 数据类或 Markdown 文档。
立即学习“Java免费学习笔记(深入)”;
// ❌ 错误写法:输出全是 3
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// ✅ 正确:用闭包捕获当前 i 值
for (var i = 0; i < 3; i++) {
(function(currentI) {
setTimeout(() => console.log(currentI), 100);
})(i);
}
// ✅ 更现代写法:用 let 声明(块级作用域本质也是闭包思想的体现)
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
配置化工厂函数
闭包适合封装带默认配置、可定制的行为,每个实例拥有独立配置副本:
function createLogger(prefix) {
const timestamp = Date.now(); // 每次调用生成独立时间戳
return function(message) {
console.log(`[${timestamp}] ${prefix}: ${message}`);
};
}
const debugLog = createLogger('DEBUG');
const errorLog = createLogger('ERROR');
debugLog('loading config'); // [171xxxxxx] DEBUG: loading config
errorLog('network failed'); // [171xxxxxx] ERROR: network failed —— 同一时间戳,但 prefix 不同
注意:timestamp 在闭包中只计算一次,确保日志头统一;而 prefix 作为参数传入,也进入闭包,实现个性化。
要点总结
- 闭包形成的关键是:内部函数在定义时所处的词法作用域中存在自由变量(未在内部函数中声明,但在其外层声明)
- 变量必须“被引用”才会被保留——如果内部函数没用到某个外层变量,引擎可能优化掉它
- 过度使用闭包可能引发内存泄漏,尤其在 DOM 事件监听或定时器中长期持有大对象时需注意清理
- ES6 的
let/const块级作用域让某些传统闭包场景简化了,但理解闭包仍是掌握异步、模块化、高阶函数的基础

















