简体   繁体   English

为什么JS需要执行上下文

[英]Why is execution context needed for JS

I am new to JS and was learning the role of execution context in JS and asked myself what is the benefit of execution context in JS and why JS is run via execution context. 我是JS新手,正在学习JS中执行上下文的作用,并问自己JS中执行上下文有什么好处,以及为什么JS通过执行上下文运行。 The second question is as we know there are global and functional execution contexts and each context has two phases creation phase and execution phase. 第二个问题是众所周知,存在全局和功能执行上下文,每个上下文都有两个阶段:创建阶段和执行阶段。 So, why we need these two phases? 那么,为什么我们需要这两个阶段? what is the point of having them. 拥有它们有什么意义?

The concept of an "execution context" provides a way of reasoning about what happens when the global environment is created and executed, or when a function is called. “执行上下文”的概念提供了一种在创建和执行全局环境或调用函数时会发生什么情况的推理方法。 It's a conceptual container for the local variables (globals, in the case of the global environment), parameters (for functions), etc. A JavaScript engine can be written without literally having any object it calls an "execution context," as long as it implements the language in keeping with the spec's defined execution context behavior. 它是局部变量(在全局环境下为全局变量),参数(对于函数)等的概念性容器。可以编写JavaScript引擎,而实际上不包含任何称为“执行上下文”的对象,只要它实现了与规范定义的执行上下文行为一致的语言。

One thing that the execution context helps explain is the behavior of closures . 执行上下文可以帮助解释的一件事是闭包的行为。 A function created within a given execution context has (conceptually) a reference to that context, even after the function call related to the context has completed: 即使在与上下文相关的函数调用完成之后,在给定的执行上下文中创建的函数也(在概念上)具有对该上下文的引用:

function foo(answer) {
    return function say() {
        console.log(answer);
    };
}
const s = foo(42);
s(); // 42

That works because the function say has a reference to the context of the call to foo that created it (well, more specifically to the thing called its "lexical environment"). 之所以可行,是因为函数say引用了创建它的foo调用的上下文(嗯,更具体地说,是称为“词法环境”的事物)。 That lexical environment continues to exist after foo returns because something still has a reference to it ( say ). foo返回之后,该词法环境仍然存在,因为仍然有引用( say )。 So the call to say afterward works. 因此,稍后say的电话有效。

The reason for having two phases is to allow use of identifiers before their declaration. 具有两个阶段的原因是允许在声明标识符之前使用标识符。 This is primarily useful for function declarations: 这主要用于函数声明:

main();

function main() {
    console.log("Worked");
}

The first phase processes the function declarations (and var statements), and then the step-by-step phase runs the code. 第一个阶段处理函数声明(和var语句),然后分步阶段运行代码。 Without the first phase, the code above would fail as of main(); 没有第一阶段,上面的代码将从main();失败main(); because main would be undeclared. 因为main不会被声明。 Of course, with the simple example above, you could just move the main(); 当然,通过上面的简单示例,您可以移动main(); call to after the function declaration, but more complicated cases become harder to solve in that way. 在函数声明调用,但是用这种方法很难解决更复杂的情况。 Languages that didn't have two phases (early C, for instance) had to provide a mechanism for "forward declaring" things that would be defined later. 没有两个阶段的语言(例如,早期的C)必须提供一种“向前声明”将在以后定义的事物的机制。 Having two phases means JavaScript doesn't have to have those. 具有两个阶段意味着JavaScript不必具有这些阶段。 (To be fair, C also differs from JavaScript in that it needs to know what all identifiers refer to during compilation, even if the identifiers are in code within functions. So it needed forward declarations just to allow foo and bar to call one another. JavaScript doesn't check the identifiers used within functions until the function is called, so some of the reasons for forward declarations in C wouldn't come up in JavaScript even if it didn't have two phases.) (公平地说,C与JavaScript的不同之处在于,即使标识符在函数内的代码中,它也需要知道所有标识符在编译过程中所引用的内容。因此,它需要向前声明,以允许foobar相互调用。 JavaScript在调用函数之前不会检查函数中使用的标识符,因此,即使JavaScript没有两个阶段,也不会在C中提出前向声明的某些原因。)

It wasn't perfectly successful. 这不是完全成功。 Having var statements initialize the variables they declare with undefined before the var statement is reached in the code was often the source of bugs and confusion: 拥有var语句初始化它们与声明变量undefined的前var达到声明中的代码是错误和混乱的经常来源:

console.log(answer); // undefined
var answer = 42;

It's easy for people to be confused by the fact that half of the var answer = 42; 人们很容易被var answer = 42; 一半 var answer = 42;所困扰var answer = 42; was done early (the var answer part), but the other half ( answer = 42; ) isn't done until later when that statement is reached. 是尽早完成的( var answer部分),但另一半( answer = 42; )直到到达该语句的较晚时间才完成。

That's why let and const create , but don't initialize , their variables during that first phase. 这就是为什么letconst在第一个阶段创建但不初始化其变量的原因。 You can use a variable above where it's declared, but only in code that runs after the initialization: 您可以在声明位置的上方使用变量,但只能在初始化后运行的代码中使用:

function foo() {
    console.log(answer);
}
// foo(); <== Would fail, `answer` isn't initialized yet
let answer = 42;
foo(); // Works, logs 42

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM