JavaScript词法作用域按函数定义位置而非调用位置查找变量,从内到外沿固化的作用域链逐层上升,同名变量以内层遮蔽外层,未声明即抛ReferenceError。

JavaScript 词法作用域决定变量查找顺序,关键看函数“写在哪”,而不是“在哪调用”。嵌套函数的变量查找,是一条从内到外、严格按代码嵌套结构逐层上升的固定路径。
查找起点:当前函数自身的词法环境
引擎先检查变量是否在当前函数内部声明(包括参数、let/const/var/function)。哪怕只是声明没赋值,也算存在;若用 let/const 声明但尚未执行到那行,会触发暂时性死区报错,不是 undefined。
- 函数参数名和 var 声明会被提升并初始化为 undefined
- let/const 声明只提升绑定,不初始化,访问即报 ReferenceError
- 未声明就直接使用,一律抛出 ReferenceError,不会回退到原型链或返回 undefined
向上推进:只沿定义时的外层作用域链走
找不到就跳到“这个函数被定义时所处的那个外层函数”里找,不是调用时所在的环境。这条链在函数创建时就固化在内部属性 [[Environment]] 中,之后永不改变。
- 比如 inner 在 outer 内部定义,那它的外层一定是 outer 的词法环境,哪怕 inner 后来被返回、传给定时器、绑成事件处理器,也依然如此
- 如果 outer 又定义在另一个 grandParent 函数里,那 inner 查找时会依次经过:inner → outer → grandParent → 全局
- 不会跳过某一层,也不会跨分支(比如不查同级的另一个函数)
遮蔽规则:同名变量以内层为准
如果内层作用域声明了与外层同名的变量,就会“遮蔽”外层变量——查找会在本层停住,不再继续往外找。
- 例如 outer 有 const x = 'outer',inner 也有 const x = 'inner',那么 inner 内访问 x 永远得到 'inner'
- 这种遮蔽是静态的,由代码书写位置决定,和运行时流程无关
- 想显式访问被遮蔽的外层变量,没有语法支持,只能改名或避免同名
终点与异常:全局之后无路可走
查到全局作用域还没找到,查找结束。此时若变量确实未声明,引擎立即抛出 ReferenceError。
- 全局变量在浏览器中对应 window 属性,但仅限 var 声明或不加关键字的赋值;let/const 不挂载,也不参与该映射
- 注意:this、arguments 等特殊绑定不属于作用域链查找范畴,它们有独立的绑定规则
- 闭包的本质,就是内层函数把这条固化的作用域链“带走了”,让外层变量即使在外部函数执行完毕后仍能被访问
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











