首页 > 解决方案 > 为什么 JS 需要执行上下文

问题描述

我是 JS 新手,正在学习 JS 中执行上下文的作用,并问自己 JS 中执行上下文的好处是什么,以及为什么 JS 是通过执行上下文运行的。第二个问题是我们知道有全局和功能执行上下文,每个上下文都有创建阶段和执行阶段两个阶段。那么,为什么我们需要这两个阶段呢?拥有它们有什么意义。

标签: javascript

解决方案


“执行上下文”的概念提供了一种推理方式,即在创建和执行全局环境或调用函数时会发生什么。它是局部变量(全局变量,在全局环境的情况下)、参数(用于函数)等的概念容器。 JavaScript 引擎可以在没有字面上称为“执行上下文”的任何对象的情况下编写,只要它实现了符合规范定义的执行上下文行为的语言。

执行上下文有助于解释的一件事是闭包的行为。在给定的执行上下文中创建的函数(在概念上)具有对该上下文的引用,即使在与上下文相关的函数调用完成之后:

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

这是有效的,因为该函数具有对创建它say的调用的上下文的引用(嗯,更具体地说,是指称为“词法环境”的事物)。foo该词法环境在返回后继续存在,foo因为某些东西仍然具有对它的引用(say)。因此,对 after 的调用say有效。

有两个阶段的原因是允许在声明之前使用标识符。这主要用于函数声明:

main();

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

第一阶段处理函数声明(和var语句),然后分步阶段运行代码。如果没有第一阶段,上面的代码会main();因为main未声明而失败。当然,通过上面的简单示例,您可以将main();调用移至after函数声明,但更复杂的情况变得更难以这种方式解决。没有两个阶段的语言(例如早期的 C)必须提供一种机制来“前向声明”稍后定义的事物。拥有两个阶段意味着 JavaScript 不必拥有这些。(公平地说,C 与 JavaScript 的不同之处还在于,它需要知道所有标识符在编译期间引用的内容,即使标识符在函数内的代码中也是如此。因此它需要前向声明以允许foobar相互调用。JavaScript 没有' 在调用函数之前不要检查函数中使用的标识符,因此即使 JavaScript 没有两个阶段,C 中前向声明的一些原因也不会出现在 JavaScript 中。)

它并不完全成功。让语句在代码中到达语句之前var初始化它们声明的变量通常是错误和混乱的根源:undefinedvar

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

人们很容易混淆一半var answer = 42;提前完成的事实(var answer部分),但另一半(answer = 42;)直到后来达到该声明时才完成。

这就是为什么在第一阶段创建但不初始化let它们的变量的原因。您可以在声明变量的位置使用上面的变量,但只能在初始化后运行的代码中使用:const

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

推荐阅读