原文作者:Valentino
原文链接:https://www.valentinog.com/blog/js-execution-context-call-stack
什么是Javascript中的执行上下文?
我打赌你不知道答案。
编程语言中最基础的组成部分是什么?
变量和函数对吗?每个人都可以学习这些板块。
但除了基础知识之外还有什么?
在称自己为中级(甚至是高级)Javascript开发人员之前,你应该掌握的Javascript的核心是什么?
有很多:Scope(作用域)
、Closure(闭包)
、Callbacks(回调)
、Prototype(原型)
等等。
但在深入研究这些概念之前,您至少应该了解Javascript引擎的工作原理。
在这篇文章中,我们将介绍每个Javascript引擎的两个基本部分:执行上下文和调用堆栈。
(不要害怕。它比你想象的容易)。
准备好了吗?
目录
- 你会学到什么?
- Javascript如何执行您的代码?
- Javascript引擎
- 它是如何工作的?
- 全局存储器?
- 什么是调用栈?
- 什么是局部执行上下文?
- 总结
你会学到什么?
在这篇文章中你将学到:
- Javascript引擎是如何工作的?
- Javascript中执行上下文
- 什么是调用栈
- 全局执行上下文和局部执行上下文之间的区别
Javascript如何执行您的代码?
通过查看Javascript内部功能,您将成为更好的Javascript开发人员,即使您无法掌握每一个细节。
现在,看看下面的代码:
var num = 2;
function pow(num) {
return num * num;
}
现在告诉我:你认为在浏览器里以何种顺序执行这段代码?
换句话说,如果您是浏览器,您将如何阅读该代码?
这听起来很简单。
大多数人认为“是的,浏览器执行功能pow并返回结果,然后将2分配给num。”
在接下来的部分中,您将发现那些看似简单的代码行背后的机制。
Javascript引擎
要了解Javascript如何运行您的代码,我们应该遇到第一件可怕的事情:
执行上下文
在Javascript中什么是执行上下文?
每次在浏览器(或Node)中运行Javascript时,引擎都会执行一系列步骤。
其中一个步骤涉及创建全局执行上下文。
什么是引擎?
也就是说,Javascript引擎是运行Javascript代码的“引擎”。
如今有两个突出的Javascript引擎:Google V8和SpiderMonkey。
V8是Google开源的Javascript引擎,在Google Chrome和Nodejs中使用。
SpiderMonkey是Mozilla的JavaScript引擎,用于Firefox。
到目前为止,我们有Javascript引擎和执行上下文。
现在是时候了解它们如何协同工作了。
它是如何工作的?
每次运行一些Javascript代码是,引擎都会创造一个全局执行上下文。
执行上下文是一个比喻的词,用于描述运行Javascript代码的环境。
我觉得你很难想象出这些抽象的东西。
现在将全局执行上下文视为一个框:
让我们再看看我们的代码:
var num = 2;
function pow(num) {
return num * num;
}
引擎如何读取该代码?
这是一个简化版本:
引擎:第一行,它是变量!让我们将它存储在全局存储器中。
引擎:第三行,我看到了一个函数声明。让我们也把它存储在全局存储器中。
引擎:看起来我已经完成了。
如果我再次问你:浏览器如何“看到”以下代码,你会怎么说?
是的,它有点自上而下......
正如你所看到的那样,引擎没有运行功能pow!
这是一个函数声明,而不是函数调用。
上面的代码将转换为存储在全局存储器中的一些值:函数声明和变量。
全局存储器?
我已经对执行上下文感到困惑,现在还要问我什么是全局存储器?
接下来让我们看看什么是全局存储器
全局存储器
Javascript引擎也有一个全局存储器。
全局内存包含全局变量和函数声明供以后使用。
如果您阅读Kyle Simpson的“作用域和闭包”,您可能会发现全局存储器与全局作用域的概念重叠。
实际上它们是一回事。
这是些很难得概念。
但你现在不应该担心。
我希望你能理解我们难题的两个重要部分。
当Javascript引擎运行您的代码时,它会创建:
- 全局执行上下文
- 全局存储器(也称为全局作用域或全局变量环境)
一切都清楚了吗?
如果我在这一点上,我会:
- 写下一些Javascript代码
- 当你是引擎时,一步一步地解析代码
- 在执行期间创建全局执行上下文和全局存储器的图形表示
您可以在纸上或使用原型制作工具编写练习。
对于我的小例子,图片看起来如下:
在下一节中,我们将看另一个可怕的事情:调用栈。
什么是调用栈?
您是否清楚地了解了执行上下文,全局存储器和Javascript引擎如何组合在一起?
如果没有,花时间查看上一节。
我们将在我们的难题中介绍另一篇文章:调用栈。
让我们首先回顾一下Javascript引擎运行代码时会发生什么。 它创建:
- 全局执行上下文
- 全局存储器
除了我们的例子,没有更多的事情发生:
var num = 2;
function pow(num) {
return num * num;
}
代码是纯粹的值分配。
让我们更进一步。
如果我调用该函数会发生什么?
var num = 2;
function pow(num) {
return num * num;
}
var res = pow(num)
有趣的问题。
在Javascript中调用函数的行为使引擎寻求帮助。
这个帮助来自Javascript引擎的朋友:调用栈。
它听起来可能并不明显,但Javascript引擎需要跟踪发生的情况。
它依赖于调用栈。
什么是Javascript中的调用栈?
调用栈就像是程序当前执行的日志。
实际上它是一个数据结构:堆栈。
调用栈的工作原理是什么?
不出所料,它有两种方法:push和pop。
push是将某些东西放入堆栈的行为。
也就是说,当您在Javascript中运行函数时,引擎会将该函数push到调用堆栈中。
每个函数调用都被push到调用栈中。
push的第一件事是main()(或global()),它是Javascript程序执行的主要线程。
现在,上一张图片看起来像这样:
pop另一端是从堆栈中删除某些东西的行为。
当函数执行结束时,将从调用栈中pop出去。
我们的调用栈将如下所示:
现在?您已准备好从那里掌握每个Javascript概念。
请看下一部分。
局部执行上下文
到目前为止,一切似乎都很清楚。
我们知道Javascript引擎创建了一个全局执行上下文和一个全局存储器。
然后,当您在代码中调用函数时:
- Javascript引擎请求帮助
- 这个帮助来自Javascript引擎的朋友:调用栈
- 调用栈会跟踪代码中调用的函数
当你在Javascript中运行一个函数时,还有另一件事情发生。
首先,该功能出现在全局执行上下文中。
然后,另一个迷你上下文出现在函数旁边:
那个迷你上下文叫做局部执行上下文。
如果您注意到,在上一张图片中,全局存储器中会出现一个新变量:var res。
变量res的值首先是undefined。
然后,只要pow出现在全局执行上下文中,该函数就会执行并且res将获取其返回值。
在执行阶段,创建局部执行上下文以保存局部变量。
记住这一点。
了解全局和局部执行上下文是掌握作用域和闭包的关键。
总结
Javascript引擎创建执行上下文,全局存储器和调用栈。但是一旦你调用一个函数,引擎就会创建一个局部执行上下文。
经常被忽视的是,新的开发人员总是将Javascript内部视为神秘的东西。
然而,它们是掌握高级Javascript概念的关键。
如果你学习执行上下文,全局存储器和调用栈,那么Scope,Closures,Callbacks和其他东西将变得轻而易举。
特别是,理解调用堆栈是至关重要的。
一旦你想象它,所有的Javascript将开始有意义:你将最终理解为什么Javascript是异步的以及我们为什么需要回调。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。