深入解析 JavaScript 执行上下文:原理、堆栈与变量提升

作者:代码拾荒时间:2026-10-22 07:53:25浏览:391

本文深入解析 JavaScript 执行上下文的核心机制,涵盖全局、函数及 Eval 三种环境。详细阐述执行上下文堆栈的单线程同步执行模型,剖析建立阶段与代码执行阶段的内部流程,包括 variableObject、作用域链及 this 的确定过程,并解释变量提升与函数声明优先级的底层原因。

本文将深入探讨 JavaScript 中最基础且核心的概念——执行上下文。阅读后,你将理解 JavaScript 引擎在执行代码前的准备工作,明白为何某些函数和变量在声明前即可被访问,以及它们最终值的定义方式。

执行上下文的概念与分类

执行上下文的概念

JavaScript 中的代码运行环境主要分为以下三类:

全局级代码:这是默认的代码运行环境。代码载入后,引擎首先进入此环境。

函数级代码:当执行一个函数时,引擎会运行该函数体内的代码。

Eval 代码:在 Eval 函数内部运行的代码所对应的环境。

网络上关于作用域的阐述众多,为便于理解,此处可将“执行上下文”视为当前代码的运行环境或作用域。以下示例展示了全局与函数级执行上下文的交互:

执行上下文的概念与分类

上图展示了四个执行上下文:紫色代表全局上下文;绿色代表 person 函数内的上下文;蓝色和橙色代表 person 函数内调用的另外两个函数的上下文。需注意,无论何种情况,全局上下文始终唯一,且可被任何其他上下文访问。例如,在 person 上下文中可以访问全局上下文中的 sayHello 变量,同样,在 firstName 或 lastName 函数中也能访问该变量。

函数上下文的数量没有限制。每次调用函数时,引擎会自动创建一个新的函数上下文,即局部作用域,用于声明私有变量等。外部上下文无法直接访问该局部作用域内的元素。在上述示例中,内部函数可以访问外部上下文声明的变量,反之则不行。这背后的原因及引擎的处理机制如下:

执行上下文堆栈与单线程模型

执行上下文堆栈

在浏览器环境中,JavaScript 引擎采用单线程工作方式。这意味着某一时刻仅有一个事件被激活处理,其他事件被放入队列等待处理。下图描述了这一堆栈机制:

执行上下文的概念与分类

已知当 JavaScript 代码文件被浏览器载入后,默认首先进入全局执行上下文。当在全局上下文中调用函数时,程序流进入被调用函数,引擎为该函数创建新的执行上下文并将其压入堆栈顶部。浏览器始终执行堆栈顶部的上下文,执行完毕后将其弹出,随后执行其下的上下文。如此依次执行并弹出,直至回到全局上下文。示例如下:


(function foo(i) {
   if (i === 3) {
    return;
   }
   else {
    foo(++i);
   }
  }(0));

上述 foo 函数通过 () 运算符强制直接运行。函数体调用了自身三次,每次局部变量 i 增加 1。每次 foo 函数被自身调用时,都会创建一个新的执行上下文。每当一个上下文执行完毕,它便从堆栈弹出,回到上一个上下文,直到再次回到全局上下文。整个过程抽象如下:

执行上下文的概念与分类

由此可见,执行上下文这一抽象概念可归纳为以下几点:

单线程模型

同步执行机制

存在唯一的全局上下文

函数执行上下文的数量无限制

每次调用函数时,都会为其创建一个新的执行上下文,即使是递归调用自身也是如此。

执行上下文的建立与执行阶段

执行上下文的建立过程

已知每次调用函数时都会创建新的执行上下文。然而,在 JavaScript 引擎内部,这一创建过程具体分为两个阶段:

建立阶段(发生在调用函数时,但在执行函数体具体代码之前)

建立变量、函数、arguments 对象及参数

建立作用域链

确定 this 的值

代码执行阶段:

变量赋值、函数引用及执行其他代码

实际上,可将执行上下文视为一个对象,其包含上述三个属性:


 (executionContextObj = {
   variableObject: { /* 函数中的arguments对象, 参数, 内部的变量以及函数声明 */ },
   scopeChain: { /* variableObject 以及所有父执行上下文中的variableObject */ },
   this: {}
   }

建立阶段与代码执行阶段的详细分析

确切地说,执行上下文对象(即上述 executionContextObj)是在函数被调用时、但在函数体真正执行前创建的。函数被调用即指前述两个阶段中的第一阶段——建立阶段。此时,引擎会检查函数中的参数、声明的变量及内部函数,并基于这些信息建立执行上下文对象。在此阶段,variableObject 对象、作用域链以及 this 指向的对象均会被确定。

上述第一阶段的具体过程如下:

定位当前上下文中调用函数的代码

在被调用函数体代码执行前,开始创建执行上下文

进入第一阶段——建立阶段:

建立 variableObject 对象:

建立 arguments 对象,检查当前上下文中的参数,建立该对象下的属性及属性值

检查当前上下文中的函数声明:

每找到一个函数声明,就在 variableObject 下以函数名建立属性,属性值为指向该函数在内存中地址的引用

若上述函数名已存在于 variableObject 下,则对应属性值会被新引用覆盖。

初始化作用域链

确定上下文中 this 指向的对象

代码执行阶段:

执行函数体中的代码,逐行运行,给 variableObject 中的变量属性赋值。

以下是一个具体的代码示例:


 function foo(i) {
   var a = 'hello';
   var b = function privateB() {
  
   };
   function c() {
  
   }
  }
  
  foo(22);

在调用 foo(22) 时,建立阶段如下:



  fooExecutionContext = {
   variableObject: {
    arguments: {
     0: 22,
     length: 1
    },
    i: 22,
    c: pointer to function c()
    a: undefined,
    b: undefined
   },
   scopeChain: { ... },
   this: { ... }
  }

由此可见,在建立阶段,除 arguments、函数声明及参数被赋予具体属性值外,其他变量属性默认均为 undefined。一旦建立阶段结束,引擎即进入代码执行阶段。该阶段完成后,上述执行上下文对象如下:


  fooExecutionContext = {
   variableObject: {
    arguments: {
     0: 22,
     length: 1
    },
    i: 22,
    c: pointer to function c()
    a: 'hello',
    b: pointer to function privateB()
   },
   scopeChain: { ... },
   this: { ... }
  }

我们看到,仅在代码执行阶段,变量属性才会被赋予具体值。

变量提升与作用域机制解析

局部变量作用域提升的原因

网上常有总结称:在函数中声明的变量及函数,其作用域提升至函数顶部,即一进入函数体即可访问其中声明的变量及函数。这是正确的,但知其然是否知其所以然?相信通过上述解释你已有所明白,此处再作分析。

看以下代码:


 (function() {
   console.log(typeof foo); // function pointer
   console.log(typeof bar); // undefined
  
   var foo = 'hello',
    bar = function() {
     return 'world';
    };
  
   function foo() {
    return 'hello';
   }
  
  }());​

上述代码定义了一个匿名函数,并通过 () 运算符强制立即执行。此时会创建一个执行上下文。我们看到示例中可立即访问 foo 及 bar 变量,并通过 typeof 输出 foo 为函数引用,bar 为 undefined。

为何我们能在声明 foo 变量前访问到它?

因为在上下文的建立阶段,先处理 arguments、参数,接着处理函数声明,最后处理变量声明。发现 foo 函数声明后,便在 variableObject 下建立 foo 属性,其值为指向函数的引用。处理变量声明时,发现 var foo 声明,但 variableObject 已具有 foo 属性,故直接跳过。进入代码执行阶段时,即可通过访问 foo 属性,因为它已存在且为函数引用。

为何 bar 是 undefined?

因为 bar 是变量声明,在建立阶段被赋予默认值 undefined。由于它仅在代码执行阶段才被赋予具体值,所以当调用 typeof(bar) 时输出值为 undefined。

至此,相信你对执行上下文已有充分理解。该概念至关重要,务必深入掌握!

以上关于 JavaScript 执行上下文的全面解析即为全部内容,希望能对大家有所帮助,也希望大家多多支持本站。

作者最新文章