203、【Agent】【OpenCode】JS 语法:作用域链与 var 循环陷阱

发布时间:2026/9/4 7:07:01
203、【Agent】【OpenCode】JS 语法:作用域链与 var 循环陷阱 【声明】本博客所有内容均为个人业余时间创作所述技术案例均来自公开开源项目如GithubApache基金会不涉及任何企业机密或未公开技术如有侵权请联系删除标题203、【Agent】【OpenCode】JS 语法作用域链与 var 循环陷阱背景上篇 blog【Agent】【OpenCode】JS 语法闭包捕获还是参数传递讲了给异步回调喂数据的两种方式与取舍闭包捕获实时引用、要求变量在回调定义作用域内vs第三参数传值注册时快照、用于循环/复用场景并给出准则——变量在作用域内 要实时值 一次性内联用闭包作用域外 要快照 复用/防泄漏用传参。上篇在讲 var 循环陷阱时甩出3 3 3 vs 0 1 2的输出但没有解释清楚为什么。本篇收束这个系列先讲清作用域链怎么判定决定闭包能访问什么再拆var 循环陷阱的完整成因OpenCode上篇留了个悬念同样是三个 setTimeout闭包版输出3 3 3、传参版输出0 1 2。要理解它得先弄懂闭包到底能访问什么——这由作用域链决定。作用域链闭包能访问什么看它写在哪闭包能访问的变量 从它定义位置沿词法作用域向上每一层能包住它的作用域里的名字模块 / 全局作用域 ← process、Rpc、Log、console… └─ 外层函数作用域 ← client、cwd、args…本案例在这层 └─ 回调自身作用域 ← 自己的参数、内部 let/var/const判定步骤拿到回调代码 → 从它定义处逐层向上查名字——自身局部 → 最近外层函数局部 → 再外层 → 全局。在链上的就能闭包捕获不在链上的只能靠参数注入这正是上篇必须传参条件①。handler:async(args){// ① handler 作用域constclientRpc.client(...)// client 在这层constcwdFilesystem.resolve(...)// cwd 在这层setTimeout((){// ② 回调定义在这client.call(checkUpgrade,{directory:cwd})// ✓ 链上可见},1000)}⚠️三个容易误解的点看定义位置不是调用位置回调哪怕 1 秒后才在事件循环里执行能访问的仍是定义时handler 里的作用域链与此刻在哪执行无关。不是最近一次调用每次 handler 执行都有一套独立的局部环境回调闭包捕获的是它定义那一次的client/cwd不同次调用互不干扰。捕获的是环境不是值闭包持有的是词法环境可变绑定所以读到的是执行时刻的最新值——这正是 var 陷阱的根源。var 循环陷阱三个条件叠加回到那个输出203 篇核心for(vari0;i3;i)setTimeout(()console.log(i),0)// 3 3 3for(vari0;i3;i)setTimeout(console.log,0,i)// 0 1 2三个条件叠加才会踩坑1.var是函数作用域不是块作用域for里的var i属于外层函数作用域整个循环只有一个i——不是每轮一个。三轮循环在几微秒内全部注册完i最后停在30→1→2→3后退出。2. 闭包捕获的是变量本身() console.log(i)只记录了要读变量 i没存值。三个回调注册时都没存只是各签了张到时读 i的欠条。3. 定时器是延迟执行的setTimeout(..., 0)排到当前任务栈清空后的宏任务队列。循环跑完、i3事件循环才开始兑现三张欠条——读到的都是 3。传参版为什么对setTimeout(console.log, 0, i)的第三参数i在注册那一刻就被拷贝存进定时器0、1、2兑现时用存好的值与循环结束后的i无关。类比闭包版 三张欠条都写按当时行情付款兑现时行情已是 3传参版 注册当场把 0、1、2 抄进纸条兑现看纸条。用 let 就没事for(leti0;i3;i)setTimeout(()console.log(i),0)// 0 1 2let是块作用域每轮循环创建一个独立的i闭包各自捕获自己的那个 → 输出 0 1 2。所以 var 陷阱的本质是var 函数作用域共享一个变量 闭包引用共享 延迟执行三件事叠加任缺其一都不会踩坑。本系列回顾篇主题一句话201JS 语法setTimeout 七种常用形式三个自由度回调写法 / 数据注入 / 生命周期202JS 语法闭包捕获还是参数传递作用域内实时值→闭包作用域外快照→传参203JS 语法作用域链与 var 循环陷阱闭包能访问什么看定义位置var 陷阱 共享变量延迟一句话记忆闭包的作用域链看定义位置自身 → 外层函数 → 全局捕获的是可变环境而非值快照var 循环陷阱 var函数作用域共享一个i 闭包引用共享 定时器延迟执行三条件叠加输出3 3 3换let或第三参数传值即解。OK本篇先到这里如有疑问欢迎评论区留言讨论祝各位功力大涨技术更上一层楼更多内容见下篇 blog【Agent】【OpenCode】TUI 内部SDK 客户端与事件流