JavaScript 中的“执行上下文”究竟是什么?

IT技术 javascript
2021-02-04 11:57:20

我的标题几乎概括了这一切。

有没有人能指教一下...

“JavaScript 中的‘执行上下文’是什么?”

以及它与“this”、提升、原型链、作用域和垃圾收集的关系?

6个回答

你问的是几个没有非常密切相关的不同概念。我将尝试简要介绍每一个。


执行上下文是语言规范中的一个概念——通俗地说——大致等同于函数执行的“环境”;也就是说,变量作用域(以及作用域链、来自外部作用域的闭包中的变量)、函数参数和this对象的值

调用堆栈是执行上下文的集合。

另请参阅此答案本文


范围的字面意思是:可以访问变量的范围。简单地说:

var x;

function a() {
    var y;
}

x可以从任何地方访问。a被调用时,x将在外部范围内。(存储在作用域链中。)

相反,y只能通过代码访问,a()因为它仅限于a的范围。这就是var关键字的作用:将变量限制在局部范围内。如果我们省略vary最终会出现在全局范围内,一般认为是一件坏事。


提升更多地视为编译时的事情。在 JavaScript 中,函数声明被“提升”到其作用域的顶部。换句话说,它们任何其他代码之前被解析和评估(这与函数表达式相反,后者是内联计算的。)请考虑以下事项:

a();
b();

function a() { }
var b = function() { }

调用a()will 会成功,因为它的声明被提升到顶部;a在程序执行开始之前自动分配给。调用b()将失败,TypeError因为b直到第 4 行才会定义。

实际上,关于你在提升方面的一点,b变量声明也将被提升:它会从一开始就被声明,但在第 4b()之前不会为其分配任何值。在第 4之前调用确实会导致错误,但是不同的是:我们将尝试执行undefined,这不是一个函数。
2021-03-27 11:57:20
我有一个与此问题相关的问题。毕竟代码执行完毕,执行上下文也从调用栈中弹出,现在假设回调队列有一些回调函数。现在,当执行上下文不存在时,该this对象如何执行诸如 setTimeout 之类的回调?它是否在 a 之前推送执行上下文cb()
2021-03-31 11:57:20
准确地说parsing只是我们的 JS 代码经历的一部分。而构成编译动作的其他部分 - 词法分析器、解析器、语义分析、优化器、代码生成器。最好从改变语义parsingcompiling 参考: stackoverflow.com/questions/1921474/...
2021-04-02 11:57:20
@josh3736 我真的很喜欢你的回答,但我不明白是什么导致堆栈像在异步函数或 while 循环中显然没有切换上下文一样?
2021-04-03 11:57:20

你问了这么多概念,但让我们一一选择并理解它们。

您的代码运行的环境是Execution context. 它是在您的代码执行时创建的。

Execution Context (Global),由 JS Engine 创建的包含 3 件重要的东西给你:

  1. 全局对象 - window
  2. 特殊对象 this
  3. 参考外部环境

让我们看一个简单的例子来理解Global Execution Context

var a = "Hello World";

function b(){

}

当 JS 引擎运行上面的代码时,它会创建以下执行上下文(如图所示): 全局执行上下文


现在让我们看看 JS 引擎是如何创建的Execution Context(然后我们将挖掘并理解提升):考虑这个场景:

b();
console.log(a);

var a = "Hello World!";
function b(){
    console.log("Called b!");
}

b()即使稍后声明,我也可以调用该函数这意味着 JS 引擎在我的代码执行之前正在做一些事情,让我们看看:

JS 引擎在执行任何代码时执行以下两个步骤:

创建阶段

  • JS 引擎解析 - 运行您的代码identifies variables & functions并由代码创建(将在执行阶段使用)
  • 为变量和函数设置内存空间 - “提升”
  • 提升- 在您的代码执行之前,JS 引擎为代码内部使用的 Var 和 Func 留出内存空间。这些变量和函数构成了要执行的任何函数的执行上下文。JS 中的所有变量最初都设置为 undefined。

执行阶段:非常容易理解,

  • 当代码逐行执行时(由 JS 解释器),它可以访问 Execution Context 中定义的变量
  • 变量赋值在这个阶段完成

只要有函数调用,就会创建一个新的执行上下文

执行上下文堆栈: 调用函数时会发生什么:

function b(){

}

function a(){
    b();
}

a();
  • 现在首先Global Execution Context要创建(如上所述)

  • 然后执行开始并遇到解释器call to function a(),并且here a new execution context is created pushed on top EC Stack

    因此,无论何时您调用一个函数,都会创建一个新的 EC 并将其放置在 EC 堆栈之上。

  • 所以现在EC for a()CREATEDinterpreeter将执行里面的代码a()行由行

  • 然后 inrepreeter 遇到call to function b(),这会创建另一个EC被推到顶部或EC堆栈

  • b()完成将弹出的出栈,然后a()将完成与一路下跌至Global EC

参见上面代码片段的执行堆栈

有关更多信息,请参阅此处davidshariff.com/blog/...
2021-04-04 11:57:20

讨论了最密切相关的主题。

执行上下文是现有代码包装器其中包含您尚未编写的代码但由JS 引擎生成

它包括以下内容——

  1. 全局对象
  2. '这'
  3. 外部环境
  4. 你的代码

每次运行 .js 文件/应用程序时都会创建一个执行上下文。这个创建阶段的第一步是提升JS引擎保留空间集的内存中定义的所有变量和函数代码。当你的代码被逐行执行时,这些就会被访问。

例如:

b();
console.log(a);
var a = "hi!";
function b() {
    console.log("calling function");
}

这里,函数 b()变量 a在定义之前都被访问过,但是,由于提升控制台不会抛出任何错误。

输出看起来像 - (试试看)

calling function
undefined

注意函数是如何完全执行的,但是我们没有为变量定义这是因为函数和变量的提升执行方式不同。函数作为一个整体被提取到内存中,但对于变量,空间被保留作为占位符,其值为undefined当引擎逐行执行您的代码时,实际值将被替换

我希望这可以为您清除概念。

我想解决

  1. 语境
  2. 这个上下文(与上下文的关系)
  3. 范围

1:执行上下文

JavaScript 是一种单线程语言,这意味着一次只能执行一个任务。当 JavaScript 解释器最初执行代码时,它默认首先进入一个全局执行上下文从这一点开始,每次调用函数都会导致创建一个新的执行上下文。

这是经常出现混淆的地方,术语执行上下文实际上是所有意图和目的,更多地指的是范围而不是上下文。这是一个不幸的命名约定,但它是 ECMAScript 规范定义的术语,所以我们有点坚持它。

每次创建新的执行上下文时,它都会附加到执行堆栈的顶部。浏览器将始终执行位于执行堆栈顶部的当前执行上下文。完成后,它将从堆栈顶部移除,并且控制将返回到下面的执行上下文。

一个执行上下文可以分为创建和执行阶段。在创建阶段,解释器将首先创建一个变量对象(也称为激活对象),该对象由执行上下文中定义的所有变量、函数声明和参数组成。从那里开始接下来初始化作用域链,最后确定 this 的值。然后在执行阶段,代码被解释和执行。

2:这个上下文

什么是“这个”上下文?上下文通常由函数的调用方式决定。当函数作为对象的方法被调用时, this 被设置为调用该方法的对象:

var obj = {
    foo: function() {
        return this;   
    }
};

obj.foo() === obj; // true

当使用 new 运算符调用函数来创建对象的实例时,同样的原则也适用。当以这种方式调用时,函数范围内的this的值将被设置为新创建的实例:

function foo() {
    alert(this);
}

foo() // window
new foo() // foo

当作为未绑定函数调用时,这将默认为浏览器中的全局上下文或窗口对象。但是,如果函数在严格模式下执行,上下文将默认为 undefined。

3 : 变量范围

变量可以在局部或全局范围内定义,这在运行时建立了变量在不同范围内的可访问性。任何定义的全局变量,意味着在函数体之外声明的任何变量将在整个运行时有效,并且可以在任何范围内访问和更改。局部变量仅存在于定义它们的函数体内,并且对于该函数的每次调用将具有不同的作用域。在那里,它仅在该调用内进行值分配、检索和操作,并且在该范围之外无法访问。

ECMAScript 6 (ES6/ES2015) 引入了let 和 const关键字来支持块范围局部变量的声明。这意味着变量将被限制在定义它的块的范围内,例如 if 语句或 for 循环,并且不能在块的开始和结束大括号之外访问。这与 var 声明相反,var 声明可在它们定义的块之外访问。 let 和 const 之间的区别在于,const 声明,顾名思义,是常量 - 对值的只读引用。这并不意味着该值是不可变的,只是变量标识符不能重新分配

对于其他主题: GC : GC Prototyping : Prototyping

“执行上下文”是一个包含所有代码以帮助管理它的保护伞。这就像管理任何环境的经理。由于在 JavaScript 应用程序中有大量的变量和函数,因此有很多词法环境,因此您需要一种方法来管理所有内容。什么是第一,什么是第二,依此类推,如果您没有“执行上下文”环境,一切都会变得糟糕。因此,将“执行上下文”视为一个包装器,一个管理您代码的管理器。