引言
执行上下文是JavaScript编程语言的最基本部分。在这篇文章中,我们将深入研究该概念以发现它,这不仅是基础知识,而且易于理解。
从学习角度的理解和复杂性来看,在许多情况下,执行上下文的概念已被投影为高级JavaScript概念。是的,如果不按正确的顺序学习适当的例子,这听起来可能很复杂。每个JavaScript初学者都需要理解为什么对这个基本概念充满信心很重要。
这是JavaScript的第二篇文章:Cracking the Nuts。如果您尚未阅读有关JavaScript解释或编译的第一篇文章,请尝试一下。希望您喜欢阅读。
如果您还没有阅读该系列的上一篇文章,则可以在这里找到:JavaScript Interpreted or Compiled? The Debate is Over。
为什么这个概念很重要?
一项研究说,人脑也许能够在其记忆中保存与整个Internet一样多的信息!但是我们不应该认为这是理所当然的,对吧?因此,一个有效的问题可能是,为什么这个概念对学习很重要?
JavaScript的执行上下文是正确理解许多其他基本概念的基础。通常,在以下每个概念中我们都会发现很多误解,只是因为我们误解了执行上下文背后的事实。
- Hoisting
- Scope
- Scope Chain
- Closure
- Event Loop
作为JavaScript开发人员,一旦我们对这些概念有了很好的了解,便可以实现以下目标:
- 🐛 Introduce lesser bugs into the Source Code.
- 👩🏫 Become a boss in doing great Code Reviews.
- 👀 Great eyes for Debugging.
- 🏭 Easier ways to tackle Production Issues.
不要不知所措
鼓励一下,如果您是新手,不要被淹没。
大多数编程语言都充满了令人沮丧的沉重行话。这些重磅词背后的基本概念大部分是简单明了且易于掌握的。以下是一些对这篇文章有用的信息:
- 解析器: 解析器或语法解析器是一个程序,可逐行读取您的代码,并了解其如何适合编程语言定义的语法以及其预期功能。正如您在我之前的文章中所看到的那样,JavaScript语法解析器采用令牌数组并创建抽象语法树(AST),以便可以对其进行进一步处理以创建可执行代码。
- 词法环境:词法一词的意思是与某事有关。词法环境是指代码的物理编写方式和位置。让我们以这段代码为例,
1 | function sayName() { |
在上面的代码中,变量名称按词法位于函数sayName
中。现在,这一点很重要,请注意,您的程序无法在计算机上按原样运行。它必须由编译器翻译。因此,编译器必须知道并映射正确地附着词法上的内容。它也必须是有意义和有效的。 请注意;通常,您的代码中将有许多词法环境。但是不会立即执行所有环境。我们很快就会看到。
- 上下文:可视化上下文一词的最好方法是,围绕您感兴趣的主题(或我们正在讨论的“上下文”)围成一个圆圈(或包装)。上下文是围绕特定事件,情况等的一组情况或事实。
- 执行上下文:这意味着当前正在运行的代码及其周围的所有内容都有助于运行它。可能有许多词法环境可用,但当前正在运行的词法环境由执行上下文管理。
这是一个图片演示,解释了同样的情况,
执行上下文
作为软件开发人员,我们喜欢(或希望)编写代码,以使其看起来不那么复杂,可以轻松维护,并遵循某些实践,标准等。以类似的方式,执行上下文允许JavaScript引擎维护代码更好地管理复杂性。
每当代码在JavaScript中运行时,它就会在执行上下文中运行,该上下文是你的代码以及JavaScript引擎完成这儿所有工作(标记化,解析,代码生成等)的组合。
全局执行上下文(GEC)
每当JavaScript代码首次运行时,它都会创建一个称为全局执行上下文(GEC)的东西。即使在.js文件中没有一行代码并进行加载时,也将创建“全局执行上下文”。
全局(Global)一词在这里是什么意思?函数外的所有事物都是全局的。
全局执行上下文也称为基本执行上下文。它为您创建了两个特殊的东西,
- 对于浏览器,全局对象被称为
window
。如果您在服务器端使用JavaScript,例如NodeJs
,则它将不是window
对象。 - 全局变量被称为
this
。
让我们通过几个示例来了解全局执行上下文,
加载空脚本
为了简单起见,让我们提取一个名为index.js的空JavaScript文件,然后将其导入名为index.html的html文件中,如下所示:
1 | <html> |
在浏览器中加载此HTML后,将不会加载和执行任何JavaScript代码。如果您打开调试器控制台(Chrome浏览器为F12)并键入this
,您将已经看到为您创建的称为this
的东西。
您也可以尝试输入window,这一次您将打印出window对象的值,
您是否注意到,窗口对象和this变量在全局执行上下文中都相等?试试这个来确认
在没有任何JavaScript代码的情况下创建全局执行上下文时,可以将其可视化为:
使用tylermcginnis.com/javascript-visualizer创建
具有变量和函数
现在让我们向JavaScript文件中添加一些代码。我们添加了一个名为name
的变量,并使用值Tom
对其进行了初始化。我们添加了一个名为sayName()
的函数,用于记录名称。
1 | var name = 'Tom'; |
您认为现在全局执行上下文会发生什么?让我们首先在下面的图片演示中看到它,然后进行解释。
全局执行上下文阶段
在全局执行上下文中创建了两个阶段,即创建和执行阶段。
创建阶段:
- 在此阶段,将创建两个特殊的东西,即用于浏览器的全局对象
window
和一个名为this
的变量。 - 为变量
name
和函数sayName()
分配内存 - 变量
name
由称为undefined
的特殊值初始化。函数sayName()
被直接放置到内存中。我们将在即将到来的文章中看到有关此概念的更多信息,该概念称为提升
(hoisting
)
- 在此阶段,将创建两个特殊的东西,即用于浏览器的全局对象
执行阶段:
- 在此阶段,实际代码执行开始。对于上面的示例,唯一发生的是将值
Tom
分配给变量name
。请注意,尽管已定义函数sayName()
,但我们并未调用它。因此,该功能将不会执行。在下一部分中,我们将学习功能执行。
函数执行上下文(FEC)
调用函数时会创建一个函数执行上下文。
让我们看下面的例子来理解这个概念。在此示例中,我们有一个名为
name
的全局变量,该变量被分配一个值Tom
。我们有一个名为tom()
的函数,该函数记录名称。最后,我们调用函数tom()
。1
2
3
4
5
6
7
8var name = 'Tom';
function tom() {
console.log(this.name + ' Runs');
}
// Invoke the function tom()
tom();请参见下面的演示,以一起了解函数执行上下文和全局执行上下文。
- 正如我们在上面看到的,全局执行上下文的创建阶段将创建
window
对象,this
变量以及该变量和函数的内存。变量使用称为undefined
的特殊值初始化。执行
阶段将值分配给变量并调用函数。接下来,创建函数执行上下文
。 - 函数执行上下文经历相同的阶段,即创建和执行。需要注意的重要一点是,函数执行上下文可以访问一个称为参数(
arguments
)的特殊变量,这是调用函数时传递给函数的参数。在我们的示例中,我们不传递任何参数。因此,长度为0。 - 一个函数可以调用另一个函数,也可以调用另一个,依此类推。对于每个函数调用,将创建一个函数执行上下文。我们将在即将发表的Scope文章中详细介绍这个概念。
那么,全局和函数执行上下文之间有什么区别?
结论
全局和函数执行上下文以及阶段(创建和执行)是需要熟悉的重要概念。这将有助于轻松理解范围,异步功能,闭合,提升等。我们将在本系列的后续文章中详细介绍每个概念。
如果您是执行上下文概念的新手,请在学习此概念的同时并排使用JavaScript Visualizer Tool。编写小函数,用户定义的变量,执行函数,并查看工具如何在执行上下文的各个阶段使用它。
后记
翻译来自Understanding JavaScript Execution Context like never before
- 在此阶段,实际代码执行开始。对于上面的示例,唯一发生的是将值