【问题标题】:How does JavaScript's lexical environment maintain variables declarations within nested block scopes?JavaScript 的词法环境如何在嵌套的块范围内维护变量声明?
【发布时间】:2021-02-07 06:18:44
【问题描述】:

我已经阅读了几篇关于执行上下文的更全面的文章,现在我有点困惑和混乱。

为了使问题尽可能简短,避免冗长的引用,我最好尝试通过一个专注于我无法理解的细节的示例来说明我的思维模式,以便您可以纠正我并指出错误。

这是一个例子:

var tomato = 'global tomato';

{
  let tomato = 'block tomato';
  console.log(tomato); // 'block tomato'
}

console.log(tomato); // 'global tomato'

到目前为止,一切都清楚了。当 JS 引擎创建一个执行上下文(在我们的例子中是全局的)时,第一行中的var tomato 声明被放入Variable Environment,而块作用域内的let tomato 进入Lexical Environment。这就解释了我们如何最终得到 2 种不同的西红柿。

现在,让我们添加另一个番茄,如下所示:

var tomato = 'global tomato';

{
  let tomato = 'block tomato';

  {
    console.log(tomato); // ReferenceError: Cannot access 'tomato' before initialization
    let tomato = 'nested block tomato';
  }

  console.log(tomato); // won't reach here
}

console.log(tomato); // won't reach here

ReferenceError 并不奇怪。事实上,我们试图在变量被初始化之前访问它,这就是所谓的时间死区。这很好地表明 JS 已经在最嵌套的块中创建了 another 变量tomato。在我们引用它的那一刻,JS 已经意识到这个 tomato 未被初始化。否则,它将从等于'block tomato' 的外部范围中获取tomato 而不会引发任何错误。所以让我们修复错误并交换行,如下所示:

var tomato = 'global tomato';

{
  let tomato = 'block tomato';

  {
    let tomato = 'nested block tomato';
    console.log(tomato); // 'nested block tomato'
  }

  console.log(tomato); // 'block tomato' - still 'block tomato'. Nothing has been overwritten.
}

console.log(tomato); // 'global tomato'

我想知道的是 JavaScript 如何管理这个最嵌套的块。因为当执行到达该行时:

let tomato = 'nested block tomato';

执行上下文的Lexical Environment 已经包含变量tomato,它在外部作用域中初始化为'block tomato'。假设 JS 不会仅为代码块创建新的执行上下文(分别使用 Lexical 和 Variable 环境)(这只是函数调用和全局脚本的情况,对吧?),显然,它不会覆盖现有的变量Lexical Environment 具有相同名称但来自嵌套块作用域的那些。正如最后一段代码所示,创建了一个全新的自变量来保存值 'nested block tomato'

那么问题是这个变量到底存储在哪里?我的意思是只有一个Lexical Environment 用于执行上下文,但我们可能会创建许多嵌套范围来声明其中的变量。我正在努力想象这些变量将存储在哪里以及整个事物如何组合在一起。

【问题讨论】:

  • 解释是,javascript首先在其环境中查找变量,然后加载它会看起来更高的范围和更高的直到全局,如果全局中不存在,那么它会给出一个未定义的错误或类似的东西
  • 这些变量都被称为tomato这一事实是无关紧要的。它们可以被称为xyz,你会得到相同的行为,只是更容易为一个人看到它们不相关。每次查找变量时,都会从当前环境开始查找,如果在当前环境中找不到,则向上遍历链式环境。两个环境之间没有冲突或混淆,因为它们是不同的变量。
  • Chrome 开发工具调试器可以向您展示作用域,也就是词法环境。

标签: javascript callstack hoisting lexical-scope lexical


【解决方案1】:

假设 JS 不会仅为代码块创建新的执行上下文(分别使用 Lexical 和 Variable 环境)(这只是函数调用和全局脚本的情况,对吧?)

这是一个错误的假设。

the specification:

词法环境是一种规范类型,用于根据 ECMAScript 代码的词法嵌套结构定义标识符与特定变量和函数的关联。一个词法环境由一个环境记录和一个对外部词法环境的可能为空的引用组成。通常,词法环境与 ECMAScript 代码的某些特定句法结构相关联,例如 FunctionDeclaration、BlockStatement 或 TryStatement 的 Catch 子句,并且每次评估此类代码时都会创建一个新的词法环境。

块语句创建一个新的词法环境。

【讨论】:

    【解决方案2】:

    在 ECMAScript 中,词法范围可以嵌套。

    我的意思是一个执行上下文只有一个词法环境,但我们可能会在其中创建许多嵌套范围来声明变量。

    不,有一个词汇环境用于……嗯……每个词汇环境。 (“范围”毕竟只是“环境”的不同术语。)

    规范不强制实现者以任何特定方式实现这一点。一种典型的方式可能是拥有环境的链表。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-03-15
      • 2010-11-23
      • 2018-08-30
      • 2020-07-31
      • 1970-01-01
      • 1970-01-01
      • 2023-03-06
      • 1970-01-01
      相关资源
      最近更新 更多