【问题标题】:ECMAScript: Where can we find specification about accessibility of let/const variablesECMAScript:我们在哪里可以找到有关 let/const 变量可访问性的规范
【发布时间】:2019-01-03 17:53:27
【问题描述】:

ECMAScript specification 中,我们在哪里可以找到关于为什么letconst 在使用BlockStatements 创建的词法环境 之外无法访问的明确说明(而不是用var) 声明的变量?

如果 BlockStatements 现在创建 new lexical environments,则 letconst 声明不应创建可在该词法环境之外访问的变量,但 var 变量应该创建。我试图了解在最新的 ECMAScript 规范中究竟在哪里指定了该行为。

来自13.3.1 Let and Const Declarations

let 和 const 声明定义了范围为正在运行的执行上下文的 LexicalEnvironment 的变量。变量是在实例化包含它们的词法环境时创建的,但在评估变量的词法绑定之前不得以任何方式访问。

来自13.3.2 Variable Statement

var 语句声明了范围为正在运行的执行上下文的 VariableEnvironment 的变量。 var 变量在其包含的词法环境被实例化时创建,并在创建时初始化为 undefined。

正如所见,两个变量声明都在其包含的词法环境被实例化时创建变量。在 BlockStatement 的情况下是编译器进入块的时间。

来自8.3 Execution Contexts

执行上下文的 LexicalEnvironment 和 VariableEnvironment 组件始终是 Lexical Environments

【问题讨论】:

  • 只是vars 在输入函数(或全局或评估)作用域时创建和初始化,而lets 和consts 在输入任何作用域时创建。它没有在任何地方明确说明它们之前没有创建和访问过。
  • 因为新的变量环境只为函数创建
  • @Bergi 谢谢。 Scope 并没有真正被规范使用。你的意思是 var 变量仅在为函数创建 Lexical Environments 时创建?如果是这样,如何在函数之外创建 var 变量?我正在尝试理解它,仅配备 ECMAScript 规范。
  • @JonasW 很有趣,谢谢。我会稍微消化一下,如果有任何疑问,我会回复。
  • @JonasW。我一直在颠倒规范,但很难发现 VariableEnvironments 只是为函数创建的。您能否添加一个答案,显示您遵循规范中的哪些步骤来得出上述结论?

标签: javascript ecmascript-6 ecmascript-2017


【解决方案1】:

正如您在var 的描述中看到的,它的作用域是正在运行的执行上下文的VariableEnvironment。有一个顶级VariableEnvironment,然后创建一个新的when you enter a function,然后在this part about Execution Contexts,它说:

执行上下文的 LexicalEnvironment 和 VariableEnvironment 组件始终是 Lexical Environments。创建执行上下文时,其 LexicalEnvironment 和 VariableEnvironment 组件最初具有相同的值。

所以,在函数的开头,LexicalEnvironment 和 VariableEnvironment 是一回事。

然后,在13.2.13 Runtime Semantics: Evaluation Block: { } 下方,您可以看到当您进入该区块时会创建一个新的LexicalEnvironment,而当您离开该区块时会恢复之前的LexicalEnvironment。但是,当你进入或离开一个块时,没有提到新的VariableEnvironment(因为它在函数中保持不变)。

因此,由于 letconst 的作用域仅限于声明它们的 LexicalEnvironment 并且是块本地的,因此在块外无法访问它。

但是,var 的作用域是 VariableEnvironment,它只创建并作用于整个函数,而不是块。

letconst 变量不能在它们的 LexicalEnvironment 之外访问,因为一旦执行上下文离开它们的块,它们的定义就不在作用域层次结构中(一旦你离开块,它们的 LexicalEnvironment 本质上就是从堆栈中弹出,不再在解释器查找变量的范围搜索链中)。


当规范添加这个时:

[letconst] 变量在实例化它们的包含词法环境时创建,但在评估变量的词法绑定之前不得以任何方式访问。

这意味着在评估它们的定义之前,即使在它们自己的词法环境中,您也被拒绝访问它们。用外行的话来说,这意味着它们不会像var 那样被提升到其范围的顶部,因此在定义之前不能使用。这是通过不初始化LexicalEnvironment 中的const 变量来实现的,直到其语句运行并且查找变量的GetBindingValue() 操作将看到它尚未初始化并抛出ReferenceError . var 变量会立即初始化为 undefined,因此它们不会导致 ReferenceError

你可以在这段代码中看到它是如何工作的:

let x = 3;

function test() {
    x = 1;
    let x = 2;
    console.log("hello");
}
test();

let x = 3 行,变量x 在外部LexicalEnvironment 中初始化。

然后,当您调用 test() 时,在该函数的开头,会创建一个新的 LexicalEnvironment,此块中 x 的新声明将放入新的 LexicalEnvironment,但尚未初始化。

然后,您将看到x = 1 语句。解释器查找x,在当前的LexicalEnvironment 中找到它,但是它没有初始化,所以它抛出了一个ReferenceError


根据您评论中的问题:

我一直在颠倒规范,但很难发现 VariableEnvironments 只是为函数创建的。您能否添加一个答案,显示您遵循规范中的哪些步骤来得出上述结论?

您必须遍历规范中创建VariableEnvironment 的所有位置,您会发现发生这种情况的唯一位置是函数执行的开始和顶层。

例如,这些地方有一个:PrepareForOrdinaryCall。还有其他几个。

但是,没有任何地方描述过块开始时发生的这种情况,只是函数的开始。

这些规范的编写方式,它们描述了事情发生的时间,而不是事情不发生的时间(这在逻辑上是有道理的),但这意味着要证明事情没有发生,你必须在任何地方都找不到说它确实发生了。

【讨论】:

  • @Magnus - 我在您最新评论中的问题答案的末尾添加了一个部分。
  • 这非常有帮助,非常感谢!我的睡眠严重逾期,所以让它消化一下,明天再详细介绍。这绝对值得深入研究。只是一个简单的初步问题:您在哪里看到有一个顶级的变量环境?我想我需要了解 JS 引擎如何解析代码,即执行上下文是如何创建的,因为它从源代码的顶部到底部工作。那会是:“15.1.10 ScriptEvaluation (scriptRecord)”:ecma-international.org/ecma-262/8.0/… ?
  • 我在上面评论中的触发器有点快。按照您建议的方法,我看到“将 scriptCxt 的 VariableEnvironment 设置为 globalEnv”。在“15.1.10 ScriptEvaluation (scriptRecord)”中。它也在这些“IteratorBindingInitializations”中完成。这与迭代器协议(即迭代器和迭代器)有关吗?
  • 我一直在解决的一个相关问题是:词法环境是否只对执行上下文重要(即仅与)有关?我看到每个函数对象还存储了一个指向它自己的封闭环境的链接(9.2 ECMAScript 函数对象)。我想这可能使运行执行上下文能够在外部 Lexical Environments 中搜索特定的 Environment Record(一直到 global environment)。此外,我猜规范中的某处说其他函数内部的函数无法访问它们的外部 Lexical Environment,因为 this 然后成为全局对象。
  • @Magnus - 我还不确定你评论的第一部分,但函数内部的函数不能通过this 访问它们的 LexicalEnvironment。他们通过LexicalEnvironments 链获得它。当你调用一个函数时,新的 LexicalEnvironment 被推到链的开头,并通过按顺序搜索该链来解析变量名。解析变量与this 无关。 this 用于this.someVar。请记住,对于诸如.call().apply() 之类的函数调用,可以使用各种工具来控制this 的值。这不是词汇。
猜你喜欢
  • 2018-11-11
  • 2014-12-07
  • 2013-07-10
  • 2023-04-10
  • 1970-01-01
  • 2022-09-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多