JavaScript中的“执行上下文”到底是什么?


71

我的头衔几乎概括了这一切。

谁能启发我...

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

以及它与“ this”,吊装,原型链,作用域和垃圾收集之间的关系?


1
您可能需要定义执行上下文
jondavidjohn 2012年

7
@jondavidjohn:执行上下文ECMA-262具有特定含义。
josh3736

6
当所有其他方法都失败时,总会有语言规范可供参考。
RobG 2014年

Answers:


63

您要问的是几个密切相关的不同概念。我将尝试简要地解决每个问题。


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

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

另请参阅此答案本文


范围从字面上看是:可以访问变量的范围。简单地说:

var x;

function a() {
    var y;
}

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

相反,y只能由代码访问,a()因为它限于a的范围。这是什么var关键词呢:限制在局部范围的变量。如果我们省略vary将最终出现在全局范围内,通常被认为是一件坏事。


提升视为更多的编译时事物。在JavaScript中,函数声明“悬挂”在其作用域的顶部。换句话说,它们是任何其他代码之前进行解析和评估的。(这与内联求值的函数表达式相反。)请考虑以下几点:

a();
b();

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

对to的调用a()将成功,因为其声明已被置于最高位置;a在程序开始执行之前被自动分配给。到的调用b()将失败,TypeError因为b直到第4行才会定义。


32
实际上,关于您的吊装工作,b变量声明也将被吊起:将从一开始就声明它,但是在第4b()行之前不会给它赋值。在第4行之前调用确实会导致错误,但是不同的是:我们将尝试执行undefined,这不是一个函数。
皮乌尔2014年

1
@ josh3736我真的很喜欢您的答案,但是我不明白是什么原因导致堆栈像在异步函数或while循环中那样切换上下文,而显然不这样做?
元帅工艺

准确地说parsing只是我们的JS代码所经历的一部分。而还有其他组成编译动作的部分-Lexer,解析器,语义分析,优化器,代码生成器。最好从改变语义parsingcompiling 参考: stackoverflow.com/questions/1921474/...
greensin

13

您问了很多概念,但让我们一个接一个地了解它们。

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

Execution Context (Global),由JS Engine创建,其中包含3个重要的内容:

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

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

var a = "Hello World";

function b(){

}

当JS Engine运行以上代码时,它将创建以下执行上下文(如图所示): 全局执行上下文


现在,让我们看看JS Engine是如何创建的Execution Context(然后我们将挖掘并了解提升):考虑以下情况:

b();
console.log(a);

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

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

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

创作阶段

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

执行阶段:非常简单易懂,

  • 当代码(由JS解释器)逐行执行时,它可以访问在执行上下文中定义的变量
  • 变量分配在此阶段完成

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

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

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 Stack的顶部。

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

  • 然后口译员遇到call to function b(),这将创建另一个EC推到顶部或EC堆栈的对象

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

有关上述代码段,请参见执行堆栈


有关更多信息,请参见davidshariff.com/blog/…–
Object.debug

5

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

执行上下文是您现有代码的包装;其中包含您尚未编写的代码; 但由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

我想解决

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

1:执行上下文

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

这就是经常引起混乱的地方,术语执行上下文实际上在所有意图和目的上都更多地是指范围而不是上下文。这是一个不幸的命名约定,但是它是ECMAScript规范所定义的术语,因此我们对此有些犹豫。

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

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

2:此上下文

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

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

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

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

function foo() {
    alert(this);
}

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

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

3:可变范围

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

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

对于其他主题: GC:GC 原型:原型


0

“执行上下文”是一个伞,它包装了所有代码以帮助对其进行管理。就像管理任何环境的经理一样。由于存在大量的词汇环境,因为在JavaScript应用程序中您拥有许多变量和函数,因此您需要一种管理所有内容的方法。首先是什么,第二是什么,依此类推,如果没有“执行上下文”环境,一切都会陷入困境。因此,请考虑将“执行上下文”包装器,即管理您的代码的管理器。


0

当您执行一个函数时,您将创建一个execution context包含称为avariable environmentthis是占位符的本地内存的新函数,它将在其执行上下文中引用.被调用该函数的位置的左侧。



-3

我想一个简单的例子可以解释一切。

注意:在以下情况下function.call(object)调用函数functionobject

// HTML

​<p id="p"></p>​​​​​​​​​​​​​​​​​​​​​​​​

// JavaScript

function helloWorld() {
    alert("HelloWorld: " + this);
}

var p = document.getElementById("p");
helloWorld();                // HelloWorld: [object DOMWindow]
helloWorld.call(window);     // HelloWorld: [object DOMWindow]
​​​​​​helloWorld.call("String");   // HelloWorld: String // Note: "toString()"
helloWorld.call​​​​(p);          // HelloWorld: [object HTMLParagraphElement]
helloWorld.call(new Date());​​​​ // HelloWorld: Tue Feb 21 2012 21:45:17 GMT+0100 (Central Europe Standard Time)

这个答案非常有用。call()是函数原型的属性。可以在任何函数上调用它。由于helloWorld()基本上对传递的对象的this属性执行var_dump(),因此对于您传递的任何值,系统都会告诉您此值。失去作用域链中的位置很容易。
Shotdsherrif 2015年

1
OP询问的是“执行上下文”而不是“上下文”。“执行上下文”在ECMAScript规范中具有非常特定的定义,包括“词法环境”,“外部”词法环境参考,“环境记录”,堆栈,程序控件以及函数的内部[[Scope]]属性。
user2312341 2015年

-3

执行上下文是包装器,可帮助管理正在运行的代码。在您的代码中,您会看到很多词法环境(这意味着之间的代码区域{ ... }),但是当前正在运行的词法环境是通过执行上下文进行管理的。它可以包含您的代码,也可以包含代码以外的内容。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.