JavaScript是怎样在栈(ECS)内运行的?——运行上下文(EC)

316 阅读3分钟

上一篇文章详细介绍了作用域,引入了Lexical Environment等术语,看过上篇文章的小伙伴们可以理解作用域的组成部分,但是JS在运行的时候,我们的作用域是一个单独的数据结构吗?运行过程中怎样伴随出栈和入栈的过程的呢?接下来我们就来看看JS在运行的时候作用域的位置。

接下来我们首先引入EC的概念,然后通过EC将作用域和栈连接起来。

Execution Context(运行上下文)

规范中规定:EC是组成JS栈的数据结构。

规范中规定EC的数据结构组成部分:

  • LexicalEnvironment:指向一个对象,这个对象记录着当前作用域下的信息。

  • VariableEnvironment:指向一个对象,这个对象记录着当前作用域下的信息。这样一来LE和VE不是相同了吗?哈哈,请参考上文。想要理解本文需要在理解上文的基础上进行,上文中详细记述了LE和VE。

  • ThisBinding:还记得我们在JS中使用的this关键字吗?我们所访问的this也是一个对象,这个对象就是ThisBinding所指的对象。

图片.png

JavaScript 作用域读到这里,估计读者就能够理解JS栈的大致情况以及作用域和栈之间是怎样关联起来的。

在ES中,规范定义了三种可执行代码,这三种代码运行的时候会创建EC。

global code:不在任何函数中的代码,例如通过script引入js文件。

function code:就是函数体中的代码。

eval code:就是eval()执行过程中运行的代码。

接下来我们通过一段代码详细了解JS的栈内动态。(为了绘图的方便,不区分LexicalEnvironment和VirableEnvironment的指向,统一指向相同的对象)

var a = 'windowa';
function fn() {
    var b = 'fnb';
}
fn();

读者可以只看简化后的内存图,真实的内存图是出于强迫症才画全的。

1. 浏览器引擎准备就绪的时候,就已经在Execution Context Stack(下文统称ECS)压入了全局对象window。

简化的内存图:

图片.png

真实的内存图(看这张图的小伙伴需要一定的基础,应该在了解了原型链,作用域链的基础上才能看懂):

图片.png

2. 提升变量声明、函数声明。

简化后的内存图:

图片.png

真实的内存图(看这张图的小伙伴需要一定的基础,应该在了解了原型链,作用域链的基础上才能看懂):

图片.png

3. 执行fn(),提升变量声明,提升函数声明,变量赋值,建立fn的运行上下文,建立fn的当前作用域。

简化后的内存图:

图片.png

真实的内存图(看这张图的小伙伴需要一定的基础,应该在了解了原型链,作用域链的基础上才能看懂):

图片.png

4. fn()执行完毕,退栈。

简化后的内存图:

图片.png

真实的内存图(看这张图的小伙伴需要一定的基础,应该在了解了原型链,作用域链的基础上才能看懂):

图片.png

5. 浏览器关闭,global Object出栈。