JavaScript 中的执行上下文

更新日期: 2022-09-14阅读: 439标签: 脚本

这是迄今为止最重要的主题之一,它可以使你对其他关键主题一目了然,例如,作用域、词法作用域、闭包和提升,而且学习JavaScript的真正工作原理很有趣。

到目前为止,在代码编辑器(Vs code )中编写的每一行混乱代码都在我们现在将讨论的这个执行上下文中运行。

坐下来,放松一下,收拾好你的美食,因为我会让你明白的。

在 JavaScript 中,一切都发生在执行上下文中,我的意思是一切。你可以将其视为评估和执行 JavaScript 代码的环境。

每当你的浏览器与任何 JavaScript 代码交叉路径时,浏览器的 JavaScript 引擎就会创建一个特殊的环境来处理此 JavaScript 代码的转换和执行。这个环境被称为执行上下文。

执行上下文包含当前正在运行的代码以及有助于其执行的所有内容。

执行上下文的类型

当你在浏览器中运行脚本时,javascript 引擎会创建不同类型的执行上下文。

全局执行上下文 (GEC)

当你第一次运行脚本或你的代码不在任何函数中时,它会被放置在全局执行上下文 (GEC) 中。

在这里,每当 JavaScript 引擎接收到一个脚本文件时,它首先会创建一个默认执行上下文,这就是我们所说的全局执行上下文 (GEC)。它是一个基本/默认执行上下文,所有不在函数内部的代码都会在其中执行。

注意:每个 JavaScript文件只有一个 GEC

函数执行上下文 (FEC)

每当你的 JavaScript 引擎遇到函数调用时,它都会在全局执行上下文中创建一种称为函数执行上下文的不同类型的 EC,以评估和执行该函数内部编写的代码。

每个函数调用都有自己的 FEC(即使你多次调用同一个函数),因此,在脚本运行时可以有多个 FEC。

它们是如何创建的?

现在执行上下文的创建分两个阶段进行:

  •  创建阶段
  •  执行阶段

1、创建阶段

在此阶段,将创建一个执行上下文对象 (ECO),其中包含我们的代码在其运行时(执行阶段)使用的重要信息/数据

属性在此对象 (ECO) 中分三个不同阶段进行设置和定义。

创建变量对象 (VO)。

创建范围链。

赋予此关键字价值。

阶段 1:变量对象的创建

变量对象就像一个在执行上下文中创建的容器,它将变量和函数声明存储在键:值对(不是函数表达式)中。

在 GEC 中,使用 var 关键字声明的每个变量都会向指向该变量的变量对象添加一个属性,并将其值设置为未定义,使用 let 或 const 声明的变量获取未初始化的值,而在函数声明中,一个属性被添加到指向该函数的变量对象中,所有的函数声明都将被存储并可以在 VO 中访问,甚至在代码开始运行之前。

在 FEC 中,不会创建此变量对象,而是构造了一个名为“argument”的类似数组的对象,其中包括提供给该函数的所有参数。

这种甚至在代码执行之前就将变量和函数(声明)存储在内存中,这就是我们所说的提升。

第 2 阶段:创建范围链

在 JavaScript 中,作用域是一种了解一段代码对脚本其他域的可访问性的方法。

每个函数执行上下文都会创建它的作用域,可以将其视为一个环境或空间,它定义的变量和函数可以通过一个称为作用域的进程来访问。

现在,当一个函数(比如 X() )在另一个函数(比如 Y() )中定义时,这个内部函数 X() 将可以访问变量,并且在外部函数 Y() 中定义的其他函数也将具有访问外部函数的代码,但事情并不止于此,它还可以访问其父元素的代码等等,直到 GCE,这种行为就是我们所说的词法作用域,但反过来不是真的。

这个作用域的概念在JavaScript 中引发了一个被称为闭包的相关现象,即使在外部函数执行完成之后,内部函数也可以访问与外部函数关联的代码……它已经死了,消失了,很久了走了。


让我们再看一个例子来理解作用域链。

let a = 10;
function first() {
 let b = 20;
 second();
 function second() {
   let c = 30;
   console.log(a + b + c);
 }
}
first(); // output is 60

这里变量 a 和 b 没有在函数 second() 中定义,它只能访问在其自己的范围(本地范围)中定义的变量 c,但是由于词法范围,它可以访问它所在的函数以及它的父母。因此,当你运行此代码时,JavaScript 引擎将无法找到变量 a 或 b,因此,它将沿着执行上下文链首先找到 b 并解析它,因为它已在函数 first() 范围内成功找到它,解析后继续查找变量 a ,JavaScript 引擎为该变量一直到全局执行上下文并解析它。

这个 JavaScript 引擎沿着不同执行上下文链向上的过程,或者我们可以说遍历执行上下文的范围以解析变量或函数调用/调用,称为 Scope Chaining。

第 3 阶段:设置“this”关键字的值

在 javascript 中,this 关键字是指执行上下文所属的范围。

在 GEC 中,这指的是一个全局对象,在浏览器的情况下是一个窗口对象。因此,在函数声明中,使用“var”关键字初始化的变量分别作为方法和属性分配给这个全局对象。

所以

var x = "hello"            
function y() {        
 console.log("hello")  
}

与以下内容相同

window.x = "hello"            
window.y = () => {        
 console.log("hello")  
}

但在 FEC 的情况下,它不会创建“this”关键字,而是可以访问定义它的环境的关键字。

执行阶段

在执行上下文的这个阶段,我们的代码开始执行,执行后从执行堆栈或调用堆栈弹出,我们将在本文后面介绍。

到目前为止,Variable 对象包含值为 undefined 和 uninitialized 的变量,具体取决于变量是分别使用 var 关键字还是使用 let/const 声明的。

这里 JavaScript引擎再次读取 EC 中的代码,用它们的实际值更新这些变量。然后代码被解析,被转译,最后被执行

执行堆栈(调用堆栈)

你有没有想过 JavaScript 引擎如何跟踪它在脚本运行时创建的各种 EC 的所有这些创建和删除?答案是执行堆栈或简单的调用堆栈。

“JavaScript 是一种同步的单线程语言”

单线程是指它只能够一次执行一个任务,一次是一行代码,而同步是指这些任务的执行以特定的顺序发生。因此,当 JavaScript 引擎读取脚本时,它会创建不同的执行上下文并将它们存储在称为调用堆栈或执行堆栈的堆栈数据结构中。

var name = "Victor";
function firstFunc() {
 var a = "Hi!";
 secondFunc();
 console.log(`${a} ${name}`);
}
function secondFunc(){
 var b = "Hey!";
 third();
 console.log(`${b} ${name}`);
}
function thirdFunc() {
 var c = "Hello!";
 console.log(`${c} ${name}`);
}
first();

当脚本在浏览器中加载时,浏览器的 JS 引擎首先会创建一个我们在上面详细介绍过的默认特殊环境,即全局执行上下文,并将其推送到此执行堆栈。

之后,当 JS 引擎发现函数调用时执行文件时,它会为其创建一个单独的函数执行上下文,如下图所示(步骤 2),并将其推送到现有默认 GEC 之上的堆栈中。


在执行 firstFunc() 时,它遇到对 secondFunc() 的调用,它暂停 firstFunc() 的执行并创建另一个 FEC 并推送到 firstFunc() FEC 顶部的堆栈,然后再次为 thirdFunc() 创建一个单独的 FEC 称呼。

顶部的 EC 将首先由 JS 引擎执行,执行完成后,它会从堆栈中弹出,并开始执行上一个活动 EC 下面的 EC,如上图所示,直到到达 GEC。

执行上下文是 JavaScript 的核心,理解它很重要,因为它可以帮助你正确理解其他主要概念。

来自:https://medium.com/@MeowMeow-afk/execution-context-in-javascript-164435701192

链接: https://www.fly63.com/article/detial/12127

javascript 3d 脚本库 - three.js的进行简要介绍

本文的目标是对three.js进行简要介绍。我们将从设置一个(案例)场景开始,使用一个旋转的立方体。在页面底部提供一个工作示例,以防您遇到困难,需要帮助。

mongodb shell 运行js脚本的四种方式

MongoDB 是一个基于分布式文件存储的数据库。是一个介于关系数据库和非关系数据库之间的产品。这篇文章讲解mongodb shell 运行js脚本的四种方式。

defer和async的区别

async 对于应用脚本的用处不大,因为它完全不考虑依赖(哪怕是最低级的顺序执行),不过它对于那些可以不依赖任何脚本或不被任何脚本依赖的脚本来说却是非常合适的,最典型的例子:Google Analytics

如何用网页脚本追踪用户

本文介绍如何编写 JavaScript 脚本,将用户数据发回服务器。数据发回服务器的常见做法是,将收集好的用户数据,放在unload事件里面,用 AJAX 请求发回服务器。

npm脚本执行多个任务

如果遇到一个类似的需求,比如执行npm publish命令前想先升级下项目的版本,基础操作是在shell中输入两次命令分别为npm run version和npm run publish,但是有没有更简单的方法呢?请看下面的内容:

8个实用Python的脚本,收藏备用

脚本写的好,下班下得早!程序员的日常工作除了编写程序代码,还不可避免地需要处理相关的测试和验证工作。例如,访问某个网站一直不通,需要确定此地址是否可访问,服务器返回什么,进而确定问题在于什么

两个实用的shell脚本实例

今天主要分享两个shell脚本实例,内容不重要,重点是看如何去实现。批量创建特殊要求用户,需求:批量创建10个系统帐号hwb01-hwb10并设置密码(密码为随机数,要求字符和数字等混合)。

如何将 FIBJS 脚本打包成 exe 可执行文件

本文将会介绍如何将 FIBJS 脚本打包成Windows 上的 exe 可执行文件。FIBJS 是一个主要为 Web 后端开发而设计的应用服务器开发框架,它建立在 Google v8 JavaScript 引擎基础上

Hershell:一款功能强大的跨平台反向Shell生成器

Hershell是一款功能强大的跨平台反向Shell生成器,该工具使用Go语言开发,基于TCP反向Shell实现其功能。该工具使用了TLS来保障数据通讯的安全性,并且提供了证书公共密钥指纹绑定功能来防止通信数据被拦截。

javascript中defer的作用是什么

defer是脚本程序强大功能中的一个“无名英雄”。它告诉浏览器Script段包含了无需立即执行的代码,并且,与SRC属性联合使用,它还可以使这些脚本在后台被下载,前台的内容则正常显示给用户。

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!