闭包是函数记住其创建时词法环境的自然结果,通过[[Environment]]内部槽实现;典型构造方式是函数工厂,如createCounter返回引用外层变量的内层函数。

闭包就是函数记住了它创建时的词法环境
闭包不是特殊语法,而是 JavaScript 引擎自然行为的结果:当一个内部函数被返回或传到外部作用域后,它仍能访问自己定义时所在作用域中的变量——哪怕外层函数早已执行完毕。function 对象内部隐含一个 [[Environment]] 内部槽,指向其创建时的词法环境,这就是闭包的底层机制。
怎么手动构造一个典型闭包
最常见写法是「函数工厂」:外层函数接收参数并定义局部变量,内层函数引用这些变量,再把内层函数返回出去。此时内层函数就形成了闭包。
function createCounter(initial) {
let count = initial;
return function() {
count++;
return count;
};
}
const counter1 = createCounter(0);
console.log(counter1()); // 1
console.log(counter1()); // 2
-
count是createCounter的局部变量,按理说执行完就该销毁 - 但
return出去的匿名函数持续引用着它,JS 引擎必须保留该变量所在的词法环境 - 每次调用
counter1()都在操作同一个count实例,说明变量被「封闭」在闭包中
闭包保护变量不等于「私有化」
闭包确实让外部无法直接访问 count,但这只是因为没提供访问接口,并非语言级访问控制。只要你在闭包内暴露方法,就能间接读写:
function createBankAccount(initialBalance) {
let balance = initialBalance;
return {
deposit: (amount) => { balance += amount; },
withdraw: (amount) => { if (amount <= balance) balance -= amount; },
getBalance: () => balance
};
}
const account = createBankAccount(100);
account.deposit(50);
console.log(account.getBalance()); // 150
console.log(account.balance); // undefined —— 外部仍不可直接访问
- 没有
let/const声明的变量不会自动进入闭包,只有被内部函数实际引用的才会被捕获 - 如果闭包里没用到某个外层变量,V8 等引擎可能在优化阶段将其丢弃(如未启用严格模式且无副作用)
- 过度使用闭包容易造成内存泄漏:比如给 DOM 元素绑定事件后又忘了移除,而事件处理器又引用了大对象
常见误判:不是所有嵌套函数都是闭包
是否构成闭包,关键看内部函数是否「逃逸」出创建它的作用域。以下情况不形成有效闭包:
立即学习“Java免费学习笔记(深入)”;
- 内部函数只在当前作用域内立即调用,比如
(function(){...})() - 内部函数虽然定义在外层,但从未被返回、赋值给全局变量、或传入异步回调等外部上下文
- 使用
var声明且被提升,但未被内部函数引用;或者引用的是全局变量而非外层局部变量
真正需要警惕的是那些隐式创建闭包却没被清理的场景,比如在循环中为每个元素绑定事件时错误地共享了同一个变量引用。











