闭包让局部变量“活下来”是因为内部函数捕获并持有外部函数的词法环境,而非复制变量;return函数是最常见构造方式,变量是引用而非快照,需防内存泄漏。

闭包为什么能让局部变量“活下来”
因为 JavaScript 的作用域链在函数定义时就确定了,不是执行时。当一个内部函数引用了外部函数的变量,并被返回或传到外部作用域,外部函数虽然执行完毕、执行上下文被销毁,但它的词法环境仍被内部函数“捕获”并保留在内存中。
关键点:不是变量被复制,而是变量所在的整个词法环境被内部函数持续持有。
return 一个函数是最常见的闭包构造方式
直接返回匿名函数或命名函数,是触发闭包最直观、最不易出错的做法。只要这个返回的函数体里访问了外层函数的参数或 let/const 声明的变量,闭包就形成了。
- 避免用
var声明闭包变量——容易因变量提升和作用域问题导致意外共享 - 不要在循环中直接返回闭包并依赖循环变量(如
for (let i = 0; i i; }是安全的,但var i就会全部输出3) - 示例:
function createCounter() { let count = 0; return function() { count++; return count; }; } const inc = createCounter(); inc(); // 1 inc(); // 2
闭包变量不是只读的,修改会影响后续调用
闭包持有的是变量的引用,不是快照。每次调用内部函数时,操作的都是同一份内存地址上的值。
- 这意味着你可以用闭包模拟私有状态、计数器、缓存等,但也意味着多个闭包实例之间互不干扰(每个
createCounter()调用生成独立的count) - 如果把闭包函数赋给全局变量或传入事件监听器,要小心内存泄漏——只要该函数还存活,它捕获的整个外层词法环境就不会被 GC 回收
- 调试时注意:Chrome DevTools 的
Scope面板里能看到Closure条目,里面列出的就是被保留的变量
箭头函数也能形成闭包,但不能用作构造器
箭头函数没有自己的 this、arguments,但它完全遵循词法作用域规则,所以只要它引用了外层变量,一样构成闭包。
- 常见误用:
const makeAdder = (x) => (y) => x + y;—— 这是合法且典型的闭包写法 - 但你不能对箭头函数使用
new,也不能用bind/call改变它的this,这点和普通函数不同 - 如果需要动态绑定
this或访问arguments,必须用function表达式,而不是箭头函数










