我的标题几乎概括了这一切。
有没有人能指教一下...
“JavaScript 中的‘执行上下文’是什么?”
以及它与“this”、提升、原型链、作用域和垃圾收集的关系?
我的标题几乎概括了这一切。
有没有人能指教一下...
“JavaScript 中的‘执行上下文’是什么?”
以及它与“this”、提升、原型链、作用域和垃圾收集的关系?
你问的是几个没有非常密切相关的不同概念。我将尝试简要介绍每一个。
执行上下文是语言规范中的一个概念——通俗地说——大致等同于函数执行的“环境”;也就是说,变量作用域(以及作用域链、来自外部作用域的闭包中的变量)、函数参数和this对象的值。
该调用堆栈是执行上下文的集合。
范围的字面意思是:可以访问变量的范围。简单地说:
var x;
function a() {
    var y;
}
x可以从任何地方访问。当a被调用时,x将在外部范围内。(存储在作用域链中。)
相反,y只能通过代码访问,a()因为它仅限于a的范围。这就是var关键字的作用:将变量限制在局部范围内。如果我们省略var,y最终会出现在全局范围内,一般认为是一件坏事。
将提升更多地视为编译时的事情。在 JavaScript 中,函数声明被“提升”到其作用域的顶部。换句话说,它们在任何其他代码之前被解析和评估。(这与函数表达式相反,后者是内联计算的。)请考虑以下事项:
a();
b();
function a() { }
var b = function() { }
调用a()will 会成功,因为它的声明被提升到顶部;a在程序执行开始之前自动分配给。调用b()将失败,TypeError因为b直到第 4 行才会定义。
你问了这么多概念,但让我们一一选择并理解它们。
您的代码运行的环境是Execution context.  
它是在您的代码执行时创建的。
Execution Context (Global),由 JS Engine 创建的包含 3 件重要的东西给你:
window this让我们看一个简单的例子来理解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 引擎在执行任何代码时执行以下两个步骤:
创建阶段:
identifies variables & functions并由代码创建(将在执行阶段使用)执行阶段:非常容易理解,
只要有函数调用,就会创建一个新的执行上下文
执行上下文堆栈: 调用函数时会发生什么:
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
我只讨论了最密切相关的主题。
执行上下文是现有代码的包装器;其中包含您尚未编写的代码;但由JS 引擎生成。
它包括以下内容——
每次运行 .js 文件/应用程序时都会创建一个执行上下文。这个创建阶段的第一步是提升。JS引擎保留空间或集的内存中定义的所有变量和函数的代码。当你的代码被逐行执行时,这些就会被访问。
例如:
b();
console.log(a);
var a = "hi!";
function b() {
    console.log("calling function");
}
这里,函数 b()和变量 a在定义之前都被访问过,但是,由于提升控制台不会抛出任何错误。
输出看起来像 - (试试看)
calling function
undefined
注意函数是如何完全执行的,但是我们没有为变量定义。这是因为函数和变量的提升执行方式不同。函数作为一个整体被提取到内存中,但对于变量,空间被保留作为占位符,其值为undefined。当引擎逐行执行您的代码时,实际值将被替换。
我希望这可以为您清除概念。
我想解决
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 应用程序中有大量的变量和函数,因此有很多词法环境,因此您需要一种方法来管理所有内容。什么是第一,什么是第二,依此类推,如果您没有“执行上下文”环境,一切都会变得糟糕。因此,将“执行上下文”视为一个包装器,一个管理您代码的管理器。