JavaScript词法作用域在函数定义时确定,而非调用时;引擎在编译阶段依据函数物理位置静态构建作用域链,与调用位置无关,闭包和块级作用域均遵循此规则。 JavaScript 中词法作用域的确定时机,其实就在函数定义时,不是什么调用时——这一点弄清楚了,很多困惑都能迎刃而解。 词法作用域在代码编写
JavaScript词法作用域的确定时机
作者:技术拾光
时间:2026-06-18
浏览:77
文章目录
JavaScript词法作用域在函数定义时确定,而非调用时;引擎在编译阶段依据函数物理位置静态构建作用域链,与调用位置无关,闭包和块级作用域均遵循此规则。

JavaScript 中词法作用域的确定时机,其实就在函数定义时,不是什么调用时——这一点弄清楚了,很多困惑都能迎刃而解。
词法作用域在代码编写阶段就已固化
JavaScript 引擎在解析源代码(也就是做词法分析和编译那一步)的时候,就已经根据函数声明在代码里的物理位置,静态地把它的作用域链搭好了。这个过程跟后面怎么调用它,完全没关系。
- 就算你把一个内部函数赋值给变量,或者传到别的地方去执行,它仍然会沿用定义时所在位置决定的那条作用域链,一点都不会变。
- 举个例子:函数
inner在outer内部声明,那它的外层作用域永远是outer的作用域。哪怕之后你在全局环境里调用inner(),它也只会往outer里找变量。 - 这种“写在哪,就属于哪”的规则,正好就是“词法”这两个字的核心含义。
与动态作用域的关键区别就在这里
有些语言(比如某些 Shell 脚本)用的是动态作用域,会在运行时根据调用栈来判定变量去哪找。但 JavaScript 不会——foo() 函数访问变量 x,只看 foo 是在哪写的,根本不看是谁调用了 foo。
- 经典例子来感受一下:
var value = 1; function foo() { console.log(value); } function bar() { var value = 2; foo(); } bar();输出结果是 1,不是 2。 - 原因很简单:
foo定义在全局,所以它向上找作用域链,直接找到了全局的value = 1。 - 即使
foo是被bar调用的,它也绝不去查bar的作用域——调用关系不影响变量查找路径。
作用域链的构建发生在函数创建时刻
每次函数对象被创建时(比如通过函数声明或者函数表达式求值),JS 引擎就会给它绑定一条作用域链。这条链由函数定义时能访问到的所有嵌套作用域组成,就像一张写好的地图,后续执行就按这张地图走。
- 全局函数 → 链上只有全局作用域。
- 函数 A 内部定义函数 B → B 的作用域链包含:B 自身作用域 + A 的作用域 + 全局作用域,一层扣一层。
- 闭包之所以能持续访问外部变量,正是因为这根链在函数创建时就确定了,而且被保留了下来——即便外部函数已经执行完毕,链上的引用还在,变量就不会被回收。
ES6 块级作用域也遵循同一原则
let 和 const 声明的块级作用域,同样是根据代码的书写结构静态确定的,没有任何例外。
- 比如:
{ let x = 1; function f() { console.log(x); } }中的f定义在块内,因此它可以访问这个块中的x。 - 反过来,如果把
f提到块外面定义,哪怕逻辑上“紧挨着”,它也访问不到块内的x。 - 关键永远都是“定义位置”,跟“靠近程度”或者“执行顺序”没有关系。
实用工具推荐
电子包浆
使用 ›绿化、压缩、噪点和虚拟水印,自由调节做旧程度。
图片压缩
使用 ›选择画质和输出格式,预览压缩前后的大小。