闭包的“记忆”功能本质是内部函数持有对外部词法环境中自由变量的实时引用,而非复制值;变量随词法环境持续存在,多个闭包可共享同一变量,且修改即时生效。

闭包的“记忆”功能,本质是函数记住了它定义时所处的作用域中的变量,即使外部函数已经执行完毕、作用域本该销毁,这些变量仍被内部函数持续持有——不是复制值,而是保留对原始变量的引用。
变量不会被回收:闭包让词法环境“活下来”
JavaScript 引擎在函数执行完后,通常会释放其执行上下文和词法环境。但若该函数返回了一个内部函数,且这个内部函数在外部被引用(比如赋值给变量或作为返回值),引擎就会保留该函数创建时的词法环境——其中包含所有自由变量(即未在内部函数中声明,但在其外层作用域中声明的变量)。
- 自由变量不是快照,而是实时可读写的引用
- 多个闭包可以共享同一组自由变量(比如多个函数由同一个外层函数返回)
- 只要任一闭包还存在,整个词法环境就不会被垃圾回收
典型例子:计数器的记忆效果
下面这段代码最直观地体现“记忆”:
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
每次调用 counter1(),它都访问并修改同一个 count 变量——这个变量就“被记住了”。注意:createCounter() 执行完后,count 并未消失,而是被返回的匿名函数“捕获”并持续持有。
“记忆”的边界:只记词法作用域,不记调用时的状态
闭包记住的是定义时的变量绑定,不是某次调用时的值。例如:
function createLoggers() {const msgs = ['a', 'b', 'c'];
const loggers = [];
for (let i = 0; i loggers.push(() => console.log(msgs[i]));
}
return loggers;
}
const [l1, l2, l3] = createLoggers();
l1(); // 'a'
l2(); // 'b'
l3(); // 'c'
这里每个闭包都记住了自己的 i(得益于 let 块级绑定),也记住了对 msgs 数组的引用。如果用 var,所有闭包会共享同一个 i,最终全输出 undefined——这恰恰反向印证了“记忆”的是变量本身,而非某次循环的瞬时值。
实用提醒:别误把“记忆”当缓存
闭包的“记忆”是作用域引用机制的自然结果,不是设计出来的缓存策略。它带来便利的同时也隐含风险:
- 容易造成意外的内存占用(比如闭包持有了大对象却长期不用)
- 修改自由变量会影响所有共享它的闭包,可能引发隐蔽副作用
- 调试时看到的变量值,是当前实时值,不是定义时的快照
理解这一点,才能真正用好闭包,而不是靠试错来“猜”它记住了什么。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











