闭包读取的是变量在词法环境中的绑定地址,而非定义时的值;let每次迭代新建绑定,故各回调输出0、1、2,var全程单绑定,故全输出3;事件循环仅控制执行时机,不改变闭包读值逻辑。

闭包对外部变量的引用,和事件循环所处阶段没有直接因果关系;它只取决于变量绑定是否被共享、以及该绑定在调用时存什么值。
闭包读取的是“绑定位置”,不是“定义时的值”
闭包不保存变量当时的值,而是记住了变量在词法环境中的内存地址(即绑定)。每次执行闭包内部代码时,都会去这个地址上读当前值——这叫“按需读取”,不是实时监听,也不是快照固化。
- 比如 let 声明的 i 在 for 循环中,每次迭代新建一个绑定,所以每个 setTimeout 回调都指向自己那一轮的 i 地址 → 输出 0、1、2
- 而 var 声明的 i 全程只有一个绑定,所有回调都读同一个地址 → 循环结束时 i 是 3,所以全输出 3
事件循环只是“触发时机”,不改变闭包的读取逻辑
setTimeout、Promise.then 这些异步操作,只是把回调推入任务队列,等同步代码跑完再执行。但闭包本身怎么读变量,早在函数定义时就由词法作用域和变量声明方式决定了。
- for 循环是同步的,瞬间执行完毕 → 此时 var i 已变成最终值,let i 则各自保留独立绑定
- 异步回调真正执行时,只是去各自捕获的绑定位置取数,不会因为“晚执行”就自动拿到新值(除非那个绑定真被改了)
真正影响“看到什么值”的,是变量是否被外部修改
只要闭包引用的绑定还存在,且有其他代码对它重新赋值,后续调用闭包就会反映更新。
- const obj = {x: 1}; 闭包里访问 obj.x → 后续 obj.x = 2,闭包再读就是 2(对象引用没变)
- 但如果写 obj = {x: 2},原闭包仍指向旧对象,不受影响
- let count = 0; 闭包里 count++ → 每次调用都改同一绑定,所以能累加
想让闭包“锁定某个值”?得手动固化
JavaScript 默认不提供值快照,需要显式处理:
- 用 IIFE 传参:
(function(val) { return () => console.log(val); })(i) - 用箭头函数参数绑定:
arr.map((_, i) => () => console.log(i)) - 用解构或展开生成新绑定:
for (let i of [...Array(3).keys()]) { setTimeout(() => console.log(i), 100); }










