JavaScript运行原理:执行上下文与作用域链详解

作者:小括号

时间:2026-10-22

浏览:584

本文深入解析JavaScript引擎的执行机制,详细阐述全局与函数执行上下文的创建过程。重点讲解变量对象(VO)在创建阶段对参数、函数声明及var变量的处理优先级,以及执行阶段通过作用域链查找标识符的具体流程,帮助开发者理解JS内存管理与闭包基础。

文章目录

JavaScript是一种基于对象、动态且弱类型的脚本语言(下文简称JS)。与Java或C++等编译型语言不同,后者在代码执行前会进行全篇编译并生成字节码(或机器码)后再执行,JS则无需编译为中间码,而是直接在浏览器环境中运行。JS的运行过程主要包含两个阶段:编译阶段和执行阶段(可参考《你不知道的JavaScript》一书)。当JS引擎遇到可执行代码时,会创建对应的执行上下文(Execution Context,简称EC)。执行上下文可理解为代码的执行环境,它由JS解释器创建并管理,用户无法直接操作该“对象”。

JS中的执行环境主要分为以下三类:

  • 全局环境:当JS引擎进入一个代码块,例如遇到<script>xxx</script>标签时,即进入全局执行环境。
  • 函数环境:当函数被调用时,在函数内部会形成一个函数执行环境。
  • eval():将字符串作为JS代码执行,官方不推荐使用此方式。

一段JS代码中可能产生多个执行上下文。在JS中,使用栈(Stack)这种数据结构来管理执行上下文。栈的特点是“先进后出,后进先出”,这种栈被称为函数调用栈。

执行上下文具有以下几个显著特点:

  • 栈底始终存放全局执行上下文,且全局执行上下文有且仅有一个。
  • 全局执行上下文只有在浏览器窗口关闭时,才会从栈中弹出。
  • 其他执行上下文的数量没有限制。
  • 栈顶始终是当前正在活动的执行上下文,其余上下文处于等待状态。一旦当前上下文执行完毕,立即弹出栈,并将控制权交还给下一个执行上下文。
  • 函数仅在每次被调用时才会为其创建执行上下文,函数声明时并不会创建。

可以将执行上下文形象地理解为一个普通的JS对象。一个执行上下文的生命周期大致包含两个阶段:

创建阶段

此阶段主要完成三件事:1、创建变量对象;2、建立作用域链;3、确定this的指向。

执行阶段

此阶段主要完成变量赋值、函数调用以及其他代码操作。

变量对象(VO)的创建过程如下:

  • 1、根据函数参数,创建并初始化arguments对象。给arguments对象添加属性“0”、“1”、“2”、“3”等,其初始值为undefined,并设置arguments.length的值为实际传入参数的个数。
  • 2、查找function函数声明,在变量对象上添加属性,属性名即为函数名,属性值为函数的引用值。如果已存在同名属性,则直接覆盖。
  • 3、查找var变量声明(查找变量时,会将函数参数等价于var声明,因此在VO中也会添加与参数名相同的属性,初始值同样为undefined)。在变量对象中添加属性,属性名为变量名,属性值为undefined。如果已存在同名属性,则不做处理。

若存在同名标识符(函数或变量),函数声明可以覆盖变量声明,即函数的优先级高于变量。

变量对象(VO)和激活对象(AO)指的是同一事物,只是在不同时期的叫法不同。在创建时期称为变量对象,在执行时期称为激活对象。

以如下代码为例进行说明:


var g_name="tom";
var g_age=20;
function g_fn(num){
 var l_name="kity";
 var l_age=18;
 function l_fn(){
  console.log(g_name + '===' + l_name + '===' + num);
 }
}
g_fn(10);

编译阶段

当JS控制器转到这段代码时,会创建一个执行上下文G_EC。

执行上下文的结构大致如下:


G_EC = {
 VO   : {},
 Scope_chain : [],
 this  : {}
}

/* VO的结构大概 */
VO = {
 g_name : undefined,
 g_age : undefined,
 g_fn : <函数在内存中引用值>
}

/* Scope_chain的大概结构如下 */
Scope_chain = [ G_EC.VO ] // 数组中第一个元素是当前执行上下文的VO,第二个是父执行上下文的VO,最后一个是全局执行上下文的VO,在执行阶段,会沿着这个作用域链一个一个的查找标识符,如果查到则返回,否知一直查找到全局执行上下文的VO

/* this */
this = undefined // 此时this的值是undefined

执行上下文一旦创建完毕,便立即被压入函数调用栈中。此时解释器会执行一个内部操作:给当前VO中的函数添加一个内部属性[[scope]],该属性指向外层的作用域链。


g_fn.scope = [ global_EC.VO ] // 该scope属性只能被JS解释器所使用,用户无法使用

执行阶段

代码一行一行执行。当遇到一个表达式时,解释器会去当前作用域链中查找VO对象。如果找到则返回结果;如果找不到,则继续查找下一个VO对象,直至全局VO对象为止。

此阶段包含变量赋值、函数调用等操作。当解释器遇到g_fn()时,识别出这是一个函数调用,随即立即为其创建一个函数执行上下文fn_EC。该上下文fn_EC同样包含两个阶段:

分别是创建阶段和执行阶段。

在创建阶段,对于函数执行上下文,在创建变量对象时,会多创建一个arguments对象,然后为arguments对象添加属性:“0”、“1”、“2”,其初始值为undefined。

  • 查找function函数声明。
  • 查找var变量声明。

实用工具推荐

电子包浆

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

使用 ›
图片压缩

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

使用 ›
相关文章