我的标题几乎概括了这一切。
有没有人能指教一下...
“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()
是CREATED
interpreeter将执行里面的代码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 应用程序中有大量的变量和函数,因此有很多词法环境,因此您需要一种方法来管理所有内容。什么是第一,什么是第二,依此类推,如果您没有“执行上下文”环境,一切都会变得糟糕。因此,将“执行上下文”视为一个包装器,一个管理您代码的管理器。