JavaScript词法作用域与作用域链深度解析

作者:半夏

时间:2026-10-23

浏览:216

本文深入解析JavaScript的词法作用域与作用域链机制。详细阐述词法作用域的四大核心规则,包括函数对外部数据的访问权限及变量遮蔽效应。通过绘制作用域链图解,演示变量在多级链式结构中的查找流程与提升策略,帮助开发者准确理解变量作用范围及闭包基础。

文章目录

本文通过具体实例,详细解析JavaScript中的词法作用域及其作用链机制,旨在为读者提供清晰的参考与理解。

一、作用域

所谓“域”即范围,也就是作用域,它界定了标识符在何处可用、何处不可用。若需深入理解,建议参阅《你不知道的JavaScript(上卷)》第一章,该书从编译原理视角阐释了作用域的本质。简而言之,作用域是一套精心设计的规则体系,用于存储变量并在后续操作中便捷地检索这些变量。

1.1 块级作用域

在C、Java、C#等编程语言中,执行如下伪代码会引发报错:


{
  var num = 123;
  {
    console.log( num );  // num => 123
   }
}
console.log( num );  //报错

1.2 JS的词法作用域

词法作用域(亦称静态作用域)是指作用范围在代码编写阶段即已确定,无需执行即可知晓。只要代码编写完成,其作用范围便已固定,这便是词法作用域的核心定义。

JavaScript中词法作用域遵循以下规则:

  1. 函数内部可以访问函数外部的数据。
  2. 在整个代码结构中,唯有函数能够限定作用域的范围。
  3. 作用域的判定首先依据提升规则进行分析。
  4. 若当前作用域内已存在同名标识,则忽略外部同名标识。

示例1:


var num = 123;
function foo(){
  console.log( num );
}
foo();  //123

示例2:


if( false ){
  var num = 123;
}
console.log( num ); //undefined

示例3:


var num = 123;
function foo () {
  var num = 456;
  function func(){
    console.log( num );
   }
   func();
}
foo();  //456

二、作用域链

只有函数能够构建作用域结构。因此,任何代码至少包含一个作用域,即全局作用域。

只要代码中存在函数,该函数便构成一个独立的作用域。若函数内部还嵌套有其他函数,则在该作用域内又可生成新的作用域。将所有此类作用域按层级排列,便形成从函数内部指向外部的链式结构,即作用域链。

例如:


function f1() {
  function f2() {
  }
}
var num = 456;
function f3() {
  function f4() {
  }
}

作用域链的结构与DOM树结构具有相似性。

2.1 绘制作用域链

绘制步骤如下:

  1. 首先将整个全局环境视为一条顶级链,记为0级链。
  2. 接着查看全局作用域中声明了哪些成员,并以方框形式将其绘制在0级链上。
  3. 随后寻找函数,由于只有函数能限制作用域,因此从函数内部引出新的链,标记为1级链。
  4. 最后,在每一个1级链内部重复上述行为,继续展开更深层的链。

一、作用域

2.2 变量的访问(搜索)规则

  1. 首先确定变量位于哪条链上,若该链上存在变量的定义与赋值,则直接使用。
  2. 若当前链未找到,则向上一级链(即 n - 1 级链)查找;若找到,则直接使用并停止查找。
  3. 若仍未找到,则继续向上层查找,直至全局链(0级)。若全局链中也不存在,则报错“is not defined”。
  4. 特别注意:同级的链之间不可混合查找。

请绘制以下程序的作用域链:


function f1() {
  var num = 123;
  function f2() {
    console.log( num ); 
   }
  f2();
}
var num = 456;
f1();  //123

一、作用域

函数f1与变量num=456位于0级链,而f1内部可展开1级链,1级链上包含num=123和函数f2。当程序调用f1()时,进入左侧1级链;由于f1内部调用了f2函数,f2中的console.log(num)可视作位于2级链。此时,程序会沿该链向上查找:首先2级链中无num,向上到达1级链,恰好1级链上有num=123,因此直接使用该值,最终打印结果为123。

2.3 如何分析代码

  1. 分析代码时,务必从代码运行的角度进行审视。若代码对变量进行了赋值,必须在图中明确标记。
  2. 若代码逻辑较为复杂,可在图中标注代码的具体内容,有时还需结合原型图与作用域图进行综合解析。

分析以下代码:


var num = 123;
function f1() {
  console.log( num );
}
function f2(){
  var num = 456;
  f1();
}
f2();  //123

作用域链图:

一、作用域

首先将num=123、函数f1和函数f2绘制在0级链上。f1中仅包含一句console语句,由此引出一条1级链;f2下方同样画出一条1级链,该链上包含num=456及函数调用语句f1();

调用f2()后,进入f2函数的作用域链;而在f2内部又调用了f1函数,程序随之进入f1的作用域链。因此,console.log(num)会在此链上查找是否存在num,若未找到,则继续向上一级链查找,恰好0级链上存在num=123,故f1函数中的console.log(num)结果为123。

感兴趣的朋友可使用在线HTML/CSS/JavaScript代码运行工具:http://tools.jb51.net/code/HtmlJsRun测试上述代码的运行效果。

更多关于JavaScript相关内容可查看本站专题:《JavaScript常用函数技巧汇总》、《javascript面向对象入门教程》、《JavaScript错误与调试技巧总结》、《JavaScript数据结构与算法技巧总结》及《JavaScript数学运算用法总结》

希望本文所述对大家JavaScript程序设计有所帮助。

实用工具推荐

电子包浆

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

使用 ›
图片压缩

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

使用 ›
相关文章