前所未有地了解JavaScript执行上下文

2021-04-04

image-20210404160631081

引言

执行上下文是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.

不要不知所措

鼓励一下,如果您是新手,不要被淹没。

overwhelmed.gif

大多数编程语言都充满了令人沮丧的沉重行话。这些重磅词背后的基本概念大部分是简单明了且易于掌握的。以下是一些对这篇文章有用的信息:

  • 解析器: 解析器或语法解析器是一个程序,可逐行读取您的代码,并了解其如何适合编程语言定义的语法以及其预期功能。正如您在我之前的文章中所看到的那样,JavaScript语法解析器采用令牌数组并创建抽象语法树(AST),以便可以对其进行进一步处理以创建可执行代码。
  • 词法环境:词法一词的意思是与某事有关。词法环境是指代码的物理编写方式和位置。让我们以这段代码为例,
1
2
3
4
function sayName() {
var name = 'someName';
console.log('The name is, ', name);
}

在上面的代码中,变量名称按词法位于函数sayName中。现在,这一点很重要,请注意,您的程序无法在计算机上按原样运行。它必须由编译器翻译。因此,编译器必须知道并映射正确地附着词法上的内容。它也必须是有意义和有效的。 请注意;通常,您的代码中将有许多词法环境。但是不会立即执行所有环境。我们很快就会看到。

  • 上下文:可视化上下文一词的最好方法是,围绕您感兴趣的主题(或我们正在讨论的“上下文”)围成一个圆圈(或包装)。上下文是围绕特定事件,情况等的一组情况或事实。
  • 执行上下文:这意味着当前正在运行的代码及其周围的所有内容都有助于运行它。可能有许多词法环境可用,但当前正在运行的词法环境由执行上下文管理。

这是一个图片演示,解释了同样的情况,

执行上下文

作为软件开发人员,我们喜欢(或希望)编写代码,以使其看起来不那么复杂,可以轻松维护,并遵循某些实践,标准等。以类似的方式,执行上下文允许JavaScript引擎维护代码更好地管理复杂性。

每当代码在JavaScript中运行时,它就会在执行上下文中运行,该上下文是你的代码以及JavaScript引擎完成这儿所有工作(标记化,解析,代码生成等)的组合。

全局执行上下文(GEC)

每当JavaScript代码首次运行时,它都会创建一个称为全局执行上下文(GEC)的东西。即使在.js文件中没有一行代码并进行加载时,也将创建“全局执行上下文”。

全局(Global)一词在这里是什么意思?函数外的所有事物都是全局的。

全局执行上下文也称为基本执行上下文。它为您创建了两个特殊的东西,

  • 对于浏览器,全局对象被称为window。如果您在服务器端使用JavaScript,例如NodeJs,则它将不是window对象。
  • 全局变量被称为this

让我们通过几个示例来了解全局执行上下文,

加载空脚本

为了简单起见,让我们提取一个名为index.js的空JavaScript文件,然后将其导入名为index.html的html文件中,如下所示:

1
2
3
4
5
6
7
8
9
<html>
<head>
<script src="index.js" />
</head>

<body>
I have loaded an empty Script
</body>
</html>

在浏览器中加载此HTML后,将不会加载和执行任何JavaScript代码。如果您打开调试器控制台(Chrome浏览器为F12)并键入this,您将已经看到为您创建的称为this的东西。

this.png

您也可以尝试输入window,这一次您将打印出window对象的值,

window.png

您是否注意到,窗口对象和this变量在全局执行上下文中都相等?试试这个来确认

this_equal_true.png

在没有任何JavaScript代码的情况下创建全局执行上下文时,可以将其可视化为:

gec.png

使用tylermcginnis.com/javascript-visualizer创建

具有变量和函数

现在让我们向JavaScript文件中添加一些代码。我们添加了一个名为name的变量,并使用值Tom对其进行了初始化。我们添加了一个名为sayName()的函数,用于记录名称。

1
2
3
4
5
var name = 'Tom';

function sayName() {
console.log(this.name);
}

您认为现在全局执行上下文会发生什么?让我们首先在下面的图片演示中看到它,然后进行解释。

全局执行上下文阶段

  • 在全局执行上下文中创建了两个阶段,即创建和执行阶段。

  • 创建阶段:

    • 在此阶段,将创建两个特殊的东西,即用于浏览器的全局对象window和一个名为this的变量。
    • 为变量name和函数sayName()分配内存
    • 变量name由称为undefined的特殊值初始化。函数sayName()被直接放置到内存中。我们将在即将到来的文章中看到有关此概念的更多信息,该概念称为提升(hoisting)
  • 执行阶段:

    • 在此阶段,实际代码执行开始。对于上面的示例,唯一发生的是将值Tom分配给变量name。请注意,尽管已定义函数sayName(),但我们并未调用它。因此,该功能将不会执行。在下一部分中,我们将学习功能执行。

    函数执行上下文(FEC)

    调用函数时会创建一个函数执行上下文。

    让我们看下面的例子来理解这个概念。在此示例中,我们有一个名为name的全局变量,该变量被分配一个值Tom。我们有一个名为tom()的函数,该函数记录名称。最后,我们调用函数tom()

    1
    2
    3
    4
    5
    6
    7
    8
    var name = 'Tom';

    function tom() {
    console.log(this.name + ' Runs');
    }

    // Invoke the function tom()
    tom();

    请参见下面的演示,以一起了解函数执行上下文和全局执行上下文。

    • 正如我们在上面看到的,全局执行上下文的创建阶段将创建window对象,this变量以及该变量和函数的内存。变量使用称为undefined的特殊值初始化。执行阶段将值分配给变量并调用函数。接下来,创建函数执行上下文
    • 函数执行上下文经历相同的阶段,即创建和执行。需要注意的重要一点是,函数执行上下文可以访问一个称为参数(arguments)的特殊变量,这是调用函数时传递给函数的参数。在我们的示例中,我们不传递任何参数。因此,长度为0。
    • 一个函数可以调用另一个函数,也可以调用另一个,依此类推。对于每个函数调用,将创建一个函数执行上下文。我们将在即将发表的Scope文章中详细介绍这个概念。

    那么,全局和函数执行上下文之间有什么区别?

    image-20210404164626206

    结论

    全局和函数执行上下文以及阶段(创建和执行)是需要熟悉的重要概念。这将有助于轻松理解范围,异步功能,闭合,提升等。我们将在本系列的后续文章中详细介绍每个概念。

    如果您是执行上下文概念的新手,请在学习此概念的同时并排使用JavaScript Visualizer Tool。编写小函数,用户定义的变量,执行函数,并查看工具如何在执行上下文的各个阶段使用它。

    后记

    翻译来自Understanding JavaScript Execution Context like never before