【问题标题】:What is the 'Execution Context' in JavaScript exactly?JavaScript 中的“执行上下文”到底是什么?
【发布时间】:2012-03-12 04:31:14
【问题描述】:

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

谁能告诉我...

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

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

【问题讨论】:

  • 你可能需要定义执行上下文
  • @jondavidjohn:执行上下文ECMA-262中具有特定含义。
  • 当一切都失败了,总有the language specification可以依靠。

标签: javascript


【解决方案1】:

您询问的是几个不太密切相关的不同概念。我将尝试简要介绍每个。


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

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

另请参阅this answerthis article


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

var x;

function a() {
    var y;
}

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

相比之下,y 只能通过a() 中的代码访问,因为它仅限于a 的范围。这就是var 关键字的作用:将变量限制在本地范围内。如果我们省略了vary 最终会进入全局范围,这通常被认为是一件坏事。


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

a();
b();

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

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

【讨论】:

  • 实际上,关于你的提升位,b 变量声明也将被提升:它将从一开始就被声明,但在第 4 行之前不会为其分配任何值。调用@第 4 行之前的 987654342@ 确实会导致错误,但会产生不同的错误:我们将尝试执行 undefined,这不是函数。
  • @josh3736 我真的很喜欢你的回答,但我不明白是什么原因导致堆栈在异步函数或 while 循环中切换上下文,而它显然没有?
  • 准确地说parsing只是我们JS代码经历的一部分。而还有其他组成编译动作的部分——词法分析器、解析器、语义分析、优化器、代码生成器。最好将语义从 parsing 更改为 compiling 参考:stackoverflow.com/questions/1921474/…
  • 我有一个与此问题相关的问题。毕竟代码执行完毕,Execution Context也从调用栈中弹出,现在假设回调队列有一些回调函数。现在,当不存在具有this 对象的执行上下文时,setTimeout 等回调如何执行?它会在cb() 之前推送执行上下文吗?
【解决方案2】:

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

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

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

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

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

var a = "Hello World";

function b(){

}

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


现在让我们看看 JS Engine 是如何创建 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 解释器),它可以 访问在执行上下文中定义的变量
  • 变量分配在这个阶段完成

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

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

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()CREATED 解释器将执行代码 里面a()一行一行

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

  • b() 完成时,它将从堆栈中弹出,然后a() 将完成 & 一直到Global EC

see Execution Stack for above code snippet

【讨论】:

【解决方案3】:

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

执行上下文是您现有代码的包装器;其中包含您尚未编写的代码;但由 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。这是因为 Hoisting 对于函数和变量的执行方式不同。函数作为一个整体被提取到内存中,但对于变量,空间被保留为 占位符,其值为 undefined。当引擎逐行执行你的代码时,实际值会被替换。

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

【讨论】:

    【解决方案4】:

    我想解决

    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

    【讨论】:

      【解决方案5】:

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

      【讨论】:

        【解决方案6】:

        当您执行一个函数时,您会创建一个新的execution context,其中包含一个称为variable environment 的本地内存和一个占位符this,它将在其执行上下文中引用@ 左侧的任何内容987654324@ 调用该函数的位置。

        【讨论】:

          【解决方案7】:

          我最近学习了全局执行上下文变量和函数。所以我的问题是函数是否有两个参数,函数参数是否有两个参数(值)。所以它在全局和调用堆栈中如何工作。这是代码

                function myfunction(a,b){
                    return a*b;
                }
                  var bana = myfunction(4,3);
                console.log(bana);
          

          【讨论】:

            【解决方案8】:

            什么是 JS 中的 EC(执行上下文)?

            简单地说,执行上下文是一个抽象概念 评估和执行 Javascript 代码的环境。 每当任何代码在 JavaScript 中运行时,它都会在执行中运行 上下文。

            更多深度详情:https://blog.bitsrc.io/understanding-execution-context-and-execution-stack-in-javascript-1c9ea8642dd0

            【讨论】:

              【解决方案9】:

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

              注意:function.call(object)object 的上下文中调用函数 function

              // 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() ,因此对于传入的任何值,您都会被告知 this 的值。...这就是 javascript 有时难以解决的问题。在作用域链中很容易失去自己的位置。
              • OP 询问的是“执行上下文”而不是“上下文”。 “执行上下文”在 ECMAScript 规范中有一个非常具体的定义,包括“词法环境”、“外部”词法环境参考、“环境记录”、堆栈、程序控制和函数的内部 [[Scope]] 属性。跨度>
              【解决方案10】:

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

              【讨论】:

                猜你喜欢
                • 2011-05-26
                • 2011-07-21
                • 2017-07-07
                • 2013-05-29
                • 2011-04-24
                • 2012-06-11
                • 2022-11-22
                • 1970-01-01
                相关资源
                最近更新 更多