在ECMAScript specification内,我们可以找到明确的规范,说明为什么在创建的词法环境之外无法访问let
和const
BlockStatements (与用var
声明的变量相反)?
如果 BlockStatements 现在创建了new lexical environments,则let
和const
声明不应创建可在该词法环境之外访问的变量,而应创建var
变量应该。我试图了解最新的ECMAScript规范在何处确切指定了该行为。
来自13.3.1 Let and Const Declarations:
let和const声明定义范围为运行中的执行上下文的LexicalEnvironment的变量。实例化包含变量的Lexical Environment时会创建这些变量,但是在评估该变量的LexicalBinding之前,不能以任何方式对其进行访问。
var语句声明范围为运行中执行上下文的VariableEnvironment的变量。 Var变量在实例化其包含的词法环境时创建,并在创建时初始化为undefined。
如图所示,两个变量声明都在实例化其包含<词>词法环境时创建变量。在 BlockStatement 的情况下,这是编译器进入该块的时间。
执行上下文的LexicalEnvironment和VariableEnvironment组件始终是Lexical Environments
答案 0 :(得分:4)
正如在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
,而离开块时则恢复了先前的VariableEnvironment
。但是,当您进入或离开一个块时,没有提及新的let
(因为该函数在函数中保持不变)。
因此,由于const
和var
的作用域是在声明它们的LexicalEnvironment中,并且位于某个块的本地,因此在该块之外无法访问它。
但是,VariableEnvironment
的作用域是let
,后者仅创建并作用于整个函数,而不是块。
const
和let
变量不能在其LexicalEnvironment外部访问,因为一旦执行上下文离开它们的块(一旦您离开该块,他们的LexicalEnvironment本质上是从堆栈中弹出的,不再在范围搜索链中供解释器查找变量。
在规范中添加以下内容:
[{
const
和var
]变量是在实例化其包含的词法环境时创建的,但在评估该变量的LexicalBinding之前,无法以任何方式进行访问。
这意味着即使在他们的词法环境中,您也无法访问它们,直到对它们的定义求值。用外行的话来说,这意味着它们不会像let
那样被提升到其作用域的顶部,因此只有在定义之后才能使用它们。这是通过在语句运行之前,不初始化const
中的LexicalEnvironment
或ReferenceError
变量来实现的,并且查找变量的GetBindingValue()
操作将发现它尚未已初始化,并将抛出var
。 undefined
变量会立即初始化为ReferenceError
,因此它们不会导致此let x = 3;
function test() {
x = 1;
let x = 2;
console.log("hello");
}
test();
。
您可以在以下代码中看到它的工作原理:
let x = 3
在x
行,在外部LexicalEnvironment中初始化了一个变量test()
。
然后,当您调用x
时,在该函数的开始处,将创建一个新的LexicalEnvironment,并将此块中LexicalEnvironment
的新声明放入该新x = 1
中,但尚未初始化。
然后,您进入x
语句。解释器查找LexicalEnvironment
,在当前的ReferenceError
中找到它,但是它尚未初始化,因此抛出了VariableEnvironment
。
根据您评论中的问题:
我一直在颠倒规格,但是很难发现VariableEnvironments仅为函数创建。也许您可以添加一个答案,以显示要遵循上述结论所遵循的规范中的哪些步骤?
您只需遍历规范中创建{{1}}的所有地方,您会发现发生此情况的唯一地方是函数执行的开始和顶层。
例如,以下位置为PrepareForOrdinaryCall。还有一些。
但是,它从来没有描述过在块的开头发生的这种情况,而只是描述函数的开头。
这些规范的编写方式,它们描述了事情何时发生而不是什么时候不发生(这在逻辑上是合理的),但这意味着要证明某事没有发生,您必须在任何地方都找不到说确实发生了。