JavaScript词法作用域的确定时机

作者:技术拾光

时间:2026-06-18

浏览:77

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

文章目录
JavaScript词法作用域在函数定义时确定,而非调用时;引擎在编译阶段依据函数物理位置静态构建作用域链,与调用位置无关,闭包和块级作用域均遵循此规则。

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。
  • 关键永远都是“定义位置”,跟“靠近程度”或者“执行顺序”没有关系。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›