JS入门难点解析4-执行上下文栈

(注1:如果有问题欢迎留言探讨,一起学习!转载请注明出处,喜欢可以点个赞哦!) (注2:更多内容请查看我的目录。)

1. 简介

在本系列的第二篇文章JS入门难点解析2-JS的变量提升和函数提升中,我们已经讨论过。之所以不说JS需要编译,只是它不像其他编译语言一样需要翻译成等价的另一种语言。但是仍然需要进行语法分析和代码生成,并且通常是立即执行。而且,JS的变量提升和函数提升就发生在编译阶段。

回顾一下:

var foo = function () {
    console.log('foo1');
}
foo();  // foo1
var foo = function () {
    console.log('foo2');
}
foo(); // foo2

以及

function foo() {
    console.log('foo1');
}
foo();  // foo2
function foo() {
    console.log('foo2');
}
foo(); // foo2

这两段代码,前一段进行了变量声明提升,后一段进行了函数声明提升。我们讲到过,这是因为 JavaScript 编译器和引擎并非一行一行地分析和执行程序,而是一段一段地分析执行。当分析执行一段代码的时候,会进行一个“准备工作”,包括变量声明提升和函数声明提升等。那么这里所谓的一段指的是什么呢?到底JavaScript编译器和引擎遇到一段怎样的代码时才会做“准备工作”呢?这就需要了解什么是可执行代码了。

2. 可执行代码

JavaScript 的可执行代码(executable code)有以下三类:全局代码、函数代码、eval代码

当JS引擎遇到这三类代码时,会开始做准备工作,创建一个“执行上下文(execution context)"。

举例说明,当JS执行到一个函数的时候,就会创建该函数的“执行上下文(execution context)"。那么问题来了,JS代码中可能出现为数众多的函数,如何管理创建的那么多执行上下文呢?

3. 执行上下文栈

JavaScript 引擎创建了执行上下文栈(Execution context stack,ECS)来管理执行上下文。

为了模拟执行上下文栈的行为,让我们定义执行上下文栈是一个数组:

ECStack = [];

试想当 JavaScript 开始要解释执行代码的时候,最先遇到的就是全局代码,所以初始化的时候首先就会向执行上下文栈压入一个全局执行上下文,我们用 globalContext 表示它,并且只有当整个应用程序结束的时候,ECStack 才会被清空,所以 ECStack 最底部永远有个 globalContext。

ECStack = [
    globalContext
];

现在 JavaScript 遇到下面的这段代码了:

function fun3() {
    console.log('fun3')
}
function fun2() {
    fun3();
}
function fun1() {
    fun2();
}
fun1();

执行一个函数的时候,就会创建一个执行上下文,并且压入执行上下文栈,当函数执行完毕的时候,就会将函数的执行上下文从栈中弹出。知道了这样的工作原理,让我们来看看如何处理上面这段代码:

// 伪代码

// fun1()
ECStack.push(<fun1> functionContext);

// fun1中调用了fun2,还要创建fun2的执行上下文
ECStack.push(<fun2> functionContext);

// fun2还调用了fun3
ECStack.push(<fun3> functionContext);

// fun3执行完毕
ECStack.pop();

// fun2执行完毕
ECStack.pop();

// fun1执行完毕
ECStack.pop();

// javascript接着执行下面的代码,但是ECStack底层永远有个globalContext

参考

JavaScript深入之执行上下文栈 JS代码执行机制

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏林欣哲

Redis知识点速查

NoSQL概述 为什么需要NoSQL 高并发读写 海量数据的高效率存储和访问 高可扩展性和高可用性 NoSQL数据库分类 键值存储。Redis 列存储。HBas...

37511
来自专栏老马说编程

(67) 线程的基本协作机制 (上) / 计算机程序的思维逻辑

上节介绍了多线程之间竞争访问同一个资源的问题及解决方案synchronized,我们提到,多线程之间除了竞争,还经常需要相互协作,本节就来介绍Java中多线程协...

2126
来自专栏Python

nginx配置 location及rewrite规则详解

1712
来自专栏程序员互动联盟

【编程基础】C语言内存使用的常见问题

所讨论的“内存”主要指(静态)数据区、堆区和栈区空间。数据区内存在程序编译时分配,该内存的生存期为程序的整个运行期间,如全局变量和static关键字所声明的静态...

4956
来自专栏灯塔大数据

每周学点大数据 | No.61磁盘算法实践(下)

NO.61 磁盘算法实践(下) Mr. 王:嗯,这是一个应用非常广泛的数据结构,跟你讲讲它的原理吧。Hash 表又叫散列表,是一种非常常见的用于实现数据字典的...

3016
来自专栏用户2442861的专栏

C语言中volatile关键字的作用

由于内存访问速度远不及CPU处理速度,为提高机器整体性能,在硬件上引入硬件高速缓存Cache,加速对内存的访问。另外在现代CPU中指令的执行并不一定严格按照顺...

1503
来自专栏烙馅饼喽的技术分享

用ECMAScript4 ( ActionScript3) 实现Unity的热更新 -- CustomYieldInstruction 自定义中断指令

ActionScript3脚本引擎为了方便热更新逻辑开发,提供的从脚本继承Unity类库功能在一些情况下可以提供开发的便利。 这次来建立一个示例,演示一下如何在...

3589
来自专栏magicsoar

C++内存布局(1)-让new出的两个变量在堆上的地址连续

大家都知道栈的地址按照从高到低的顺序增长的, 而堆的地址是按照从底到高的顺序增长的。 int *n1 = new int(1); int *n2 = new i...

2099
来自专栏LEo的网络日志

python技巧分享(九)

3546
来自专栏java思维导图

值得收藏!Redis五大数据类型应用场景(一)

Redis开创了一种新的数据存储思路,使用Redis,我们不用在面对功能单调的数据库时,把精力放在如何把大象放进冰箱这样的问题上,而是利用Redis灵活多变的数...

2624

扫码关注云+社区

领取腾讯云代金券