2017-08-02 78 views
1

宣称“让”变量考虑一下:的ReferenceError在外部范围

'use strict'; 
 

 
{ 
 
    let p = 1; 
 
    { 
 
    console.log(p); 
 
    let p = 2; 
 
    } 
 
}

一个直觉告诉我们应该记录为“1”(因为VAR必须保留其旧重新宣布之前的价值)。但是,实际结果是ReferenceError。这是为什么? (基于标准的解释将被赞赏)。

请注意,我已经在外部范围声明了p,所以它在内部块中已知。如果你注释掉p=2这一行,一切正常。

作为验尸笔记,虽然这种行为似乎被记录,但它仍然是非常直观的,参见。这个C代码:

void main() { 
    int p = 1; 
    { 
    printf("%d\n", p); // prints '1' 
    int p = 2; 
    } 
} 

另一个JS fuckup特点要记的!

+3

_“如果你注释掉p = 2的行,那么一切正常。”_当然,它确实......你没有一个冲突的块范围变量。 –

回答

10

根据MDN

在ECMAScript中2015,let绑定不受可变吊装,这意味着让声明不移动到当前执行上下文的顶部。在初始化之前引用块中的变量会导致ReferenceError(与使用var声明的变量相反,该变量仅具有未定义的值)。变量处于从块开始到“初始化”处理的“时间死区”。

问题是你的let p语句创建一个新的变量,其范围是整个代码块。因此,在console.log中的p(p);指的是新创建的变量。

相似于您的情况提供一个例子:

function test(){ 
    var foo = 33; 
    if (true) { 
    let foo = (foo + 55); // ReferenceError 
    } 
} 
test(); 

由于词法作用域,标识符“foo”的表达内侧(FOO + 55)的计算结果为,如果块的FOO,而不是覆盖变量foo,其值为33. 在该行中,if语句块的“foo”已经在词法环境中创建,但尚未达到(并终止)其初始化(这是语句本身的一部分) :它仍然处于暂时的死亡地带。

+0

没有什么可说的:完美的答案;-) – trincot

+0

我编辑了这个问题,使我的观点清晰。 – georg

+0

这是更完美的MDN文章;) – Stephan