5
JavaScript 是如何工作的系列——第三篇

前言

在上篇文章《JavaScript 引擎(V8)是如何工作的》中,我们介绍了 JavaScript 引擎(V8)是如何执行 JavaScript 代码的。本篇文章将开始介绍 JavaScript 执行机制中的核心概念——执行上下文。

执行上下文

执行上下文(Execution Context),也称为执行环境,就是当前 JavaScript 代码执行时所在环境的抽象概念。

每当开始执行代码时,JavaScript 引擎会为整个程序创建一个执行上下文(全局执行上下文),JavaScript 代码都是在执行上下文中运行的。

在 JavaScript 中,执行环境主要分为:

  • 全局执行上下文(Global Execution Context): 全局执行上下文是最顶层的一个执行环境,是 JavaScript 代码最开始运行时的默认环境。一个程序中只会有一个全局执行上下文。
  • 函数执行上下文(Function Execution Context):在JavaScript中,每个函数都有自己的执行上下文。每当一个函数被调用时,就会为该函数创建一个新的执行上下文。一个程序中可以有多个函数执行上下文。函数执行上下文可以访问全局执行上下文,反之则不行。
  • eval 函数执行上下文: eval 函数在执行其内部代码时,也会创建属于自己的执行上下文(因为 eval 函数并不建议使用,所以这里不做讨论)。

我们看一个例子:

var a = 'Hello World!'

function helloWorld () {
    var a = 'Hello Function!'
    console.log(a) 
}

helloWorld()
console.log('a) 

上述代码,创建的执行上下文结构如下图所示:
Image  7

显而易见,在一个 JavaScript 程序中,通常情况下都会存在多个执行上下文,那么这些执行上下文是如何管理的呢?

JavaScript 引擎创建了执行上下文栈(Execution Context Stack) 来管理执行上下文。

执行上下文栈

首先我们来了解下什么是栈数据结构

栈中数据的存取方式类似给枪上子弹,先上的子弹最后打出,后上的子弹先打出。
特点:先进后出,后进先出

了解了栈数据结构后,我们继续来说执行上下文栈是如何管理执行上下文的?

我们用数组来模拟执行上下文栈的行为:ECStack = [];

function fn2() {
  console.log('fn2')
}

function fn1() {
  console.log('fn1')
  fn2();
}

fn1();

上述这段代码在执行过程中,执行上下文栈的行为是什么样的?我们来分析一下:

  1. 在代码开始执行时,首先会创建全局执行上下文并将其推入到执行上下文栈中;
ECStack.push(global_EC);
  1. 全局上下文入栈后,其中的可执行代码开始从顶部按顺序逐行解释执行,当调用函数 fn1时,会创建 fn1 函数执行上下文并将其推入到执行上下文栈中;
ECStack.push(fn1_EC);
  1. 以此类推,当开始执行 fn2 时,会创建 fn2 函数执行上下文并入栈;此时所有的执行上下文都已经入栈;
ECStack.push(fn2_EC);

Image  8

  1. 当 fn2 执行完毕,fn2 函数执行上下文出栈;
ECStack.pop();
  1. 当 fn1 执行完毕,fn1 函数执行上下文出栈;
ECStack.pop();
  1. 当所有代码执行完毕,就又回到了全局执行上下文;当程序退出时,即关闭浏览器或网页时,全局执行上下文才出栈。
ECStack.pop();

Image13

总结:

  • JavaScript 是单线程同步执行的,只有栈顶的上下文处于执行中,其他上下文需要等待;
  • 全局执行上下文永远第一个入栈,也就是说它永远在栈底;而当前执行的函数执行上下文永远最后一个入栈,也就是说它永远在栈顶。
  • 执行上下文出栈后,保存在该环境中的所有变量和函数定义也随之销毁。

下一篇

在每个执行上下文中,都包括三个重要的属性:(ES3版,ES5后做了一些变更,具体变化内容后续会写文章总结)

  • 变量对象(Variable Object,VO)
  • 作用域链(Scope Chain)
  • this指向

Image14

伪代码如下:

global_EC = {
    scopeChain: {
        // Current scope + scopes of all its parents
    },
    variableObject: {
        // All the variables including inner variables & functions, function arguments
    },
    this: {}
}

下篇文章将开始介绍 JavaScript 的作用域,以及执行上下文中的作用域链。
传送门:《JavaScript 之作用域与作用域链》

参考:

JavaScript深入之执行上下文
前端基础进阶(二):执行上下文详细图解
JS 执行环境、作用域链、活动对象
The Journey of JavaScript From Downloading Scripts to Execution – Part III

Jojo
126 声望12 粉丝

Stick a little bit more every day