词法作用域在函数定义时就确定了作用域链的骨架,与调用位置无关;查找变量时沿[[Environment]].Outer逐级向上匹配,直至全局;let/const受TDZ限制,var会提升;闭包是函数携带定义时环境链的自然结果。

变量的词法定位,本质就是 JavaScript 在定义函数那一刻就“拍板”了它将来找变量的路线图——这条路线,就是作用域链。它不看函数在哪被调用,只认函数在哪被写出来。
词法作用域决定查找起点
词法作用域是静态的,由代码书写结构直接决定。比如一个函数写在全局里,它的外层就是全局环境;如果写在一个函数内部,它的外层就是那个父函数的作用域。这个嵌套关系在函数对象创建时就被固化进 [[Environment]] 内部属性中,运行时不会改变。
- 函数定义位置 → 决定其作用域链的“骨架”
- 调用位置无关紧要 → 不会切换外层环境
- 哪怕把 inner() 拿到全局去调用,它依然沿 original outer → global 查找
作用域链是逐级向上匹配的路径
当执行到某行访问变量的代码(如 console.log(x)),引擎立刻启动查找流程:从当前执行上下文的词法环境记录开始,一层层顺着 Outer 引用往上翻,直到找到第一个匹配的标识符声明。
AI 舌诊面诊问诊医疗系统 + 诊疗商城系统源码是一套结合 AI 辅助诊断与商品转化的健康类程序,基于浩创云、ThinkPHP(PHP)开发,支持微信服务号与 H5 端,适合做中医舌诊面诊问答、健康建议推送以及配套诊疗商品的运营。
- 先查当前环境记录(如 inner 的 EnvironmentRecord)
- 没找到就跳到 [[Environment]].Outer(即 outer 的词法环境)
- 再没找到就继续跳,最终落到全局环境记录
- 全程找不到 → 抛出 ReferenceError
let/const 和 var 的差异暴露查找细节
变量声明方式直接影响查找过程中的“可见性窗口”,这反过来印证了作用域链的严格层级性:
- var 声明会被提升并初始化为 undefined,所以查找能在当前 AO 中命中,但值可能不是预期的
- let/const 存在暂时性死区(TDZ),即使作用域链能到达该层,只要声明语句还没执行,访问就报错 —— 这说明查找成功了,但“可访问性”还受声明状态约束
- 块级作用域(如 if {})对 let/const 生效,意味着新的词法环境被推入链中,而 var 会忽略块,直接绑定到函数或全局环境
闭包是作用域链的自然结果
当一个内部函数被返回或传出,它携带的 [[Environment]] 仍指向定义时的外层词法环境。即便外层函数执行结束、AO 理论上应被销毁,JavaScript 引擎也会保留那些被闭包引用的环境记录。
- 闭包不是特殊语法,而是作用域链 + 引用保持的组合效果
- 每个闭包实例都拥有自己独立的作用域链快照
- 多个同级 inner 函数,各自捕获的是同一份 outer 环境(共享 outerVar),而非副本










