最后的行(尚未执行)怎么可能影响代码的开头?为什么它会抛出不正确的错误?
How is it possible that lines in the end (which were not executed yet) affect the beginning of the code? And why does it throw an incorrect error?
所以我的代码中有一个 x is not defined
错误,这让我有点困惑,因为 x
之前已经定义了几行。我不得不花一些时间调整我的代码,删除和添加行,直到我设法理解它发生的原因。在我删除所有不必要的信息后,现在的代码如下所示:
let foo = 2;
console.log(foo);
if (foo === 2){
console.log(foo);
let foo = 1;
}
它在第 5 行抛出 foo is not defined
。当我尝试 console.log(foo)
时弹出错误!如果我删除第 6 行 let foo = 1;
代码工作正常。我的意思是在我第二次声明 foo
之前发生了错误。所以第一个问题是:
- 第 6 行(尚未执行)怎么可能使第 5 行以错误结束?
我不明白的第二件事是为什么它说 foo is not defined
而不是 foo has been already declared
。
如果我将第二个 let
替换为 var
,第 6 行将出现一个错误,它会显示 foo has been already declared
,所以它看起来不错。但是将 let
设置为第二个标识符总是会引发不正确的错误。
- 为什么会抛出不正确的错误?
在测试不同的场景后,我注意到结果取决于我使用的标识符:
identifiers | result
----------------------------------------------
var var | the code works well
var let | not defined error
let var | has been already declared error
let let | not defined error
所以第三题是:
- 为什么每个人都反对使用
var
,而在这种情况下双重使用 var
是代码完美运行的唯一方法?是例外吗?
- How is it possible that line 6 (which hasn't been executed yet) makes line 5 end up with an error?
因为用 let
、const
和 class
声明的绑定范围(松散地,"variables")是整个块,而不仅仅是它们所在的位置重新声明到块的末尾。从代码进入块到执行let
语句之间的时间称为临时死区(TDZ),在此期间绑定存在但未初始化且无法使用以任何方式。即使在代码流中遇到 let foo
之前,块中的 let foo
也会遮挡外部 foo
。
除了范围之外,这个 TDZ 是 var
和 let
之间的最大区别在于 var
创建绑定 并且 初始化它到 undefined
,无论 var
语句在范围内的哪个位置。相反,let
(以及 const
和 class
)创建绑定,但 不 初始化它直到稍后,当 let
(const
, class
) 在逐步执行代码中遇到。您不能使用未初始化的绑定。
- Why does it throw an incorrect error?
没有错。你可以说它措辞不当。 :-) 基本上是说 "you can't use foo
here, it's not initialized." 来自 V8 的当前错误消息(Chrome 中的 JavaScript 引擎、Chromium、Brave、新的基于 Chromium 的 Edge 和 Node.js)在我看来,更清晰:
Uncaught ReferenceError: Cannot access 'foo' before initialization
当您使用 let
声明变量时,它在当前代码块的范围内有效。您的第二个 let foo
声明定义了一个与第一个变量不同的 foo
并且它仅在 if 块中有效。但是,您在定义它之前使用它,因此您会正确地得到它尚未定义的错误。
如果您确实打算有两个不同的 foo
变量,我建议您将它们命名为其他名称(例如 foo1 和 foo2)以避免冲突。然后很明显您在定义变量之前就在使用它。
let foo1 = 2;
console.log(foo1);
if (foo1 === 2){
console.log(foo1);
let foo2 = 1;
}
如果您的意思是第 5 行使用 foo
的第一个实例设置为 2,那么您已经通过 if 代码块中发生的新定义隐藏了它。
如果您的意思是要在第 5 行使用设置为 1 的 foo
,那么您应该将其定义移至使用前。
请注意,使用 var
会产生不同的结果,因为 var
变量的范围比 let
变量的范围更广。请参阅具有此定义的 here:
let allows you to declare variables that are limited to a scope of a
block statement, or expression on which it is used, unlike the var
keyword, which defines a variable globally, or locally to an entire
function regardless of block scope.
为了让它更清楚,我在代码的每个阶段用变量的状态标记了你的代码:
let foo = 2; // foo defined and set to 2
console.log(foo); // foo defined and set to 2
if (foo === 2) // foo defined and set to 2
{ // <-- start of the if-block!
console.log(foo); // foo not defined yet
let foo = 1; // foo defined and set to 1
} // <-- end of if-block!
console.log(foo); // foo defined and set to 2
所以我的代码中有一个 x is not defined
错误,这让我有点困惑,因为 x
之前已经定义了几行。我不得不花一些时间调整我的代码,删除和添加行,直到我设法理解它发生的原因。在我删除所有不必要的信息后,现在的代码如下所示:
let foo = 2;
console.log(foo);
if (foo === 2){
console.log(foo);
let foo = 1;
}
它在第 5 行抛出 foo is not defined
。当我尝试 console.log(foo)
时弹出错误!如果我删除第 6 行 let foo = 1;
代码工作正常。我的意思是在我第二次声明 foo
之前发生了错误。所以第一个问题是:
- 第 6 行(尚未执行)怎么可能使第 5 行以错误结束?
我不明白的第二件事是为什么它说 foo is not defined
而不是 foo has been already declared
。
如果我将第二个 let
替换为 var
,第 6 行将出现一个错误,它会显示 foo has been already declared
,所以它看起来不错。但是将 let
设置为第二个标识符总是会引发不正确的错误。
- 为什么会抛出不正确的错误?
在测试不同的场景后,我注意到结果取决于我使用的标识符:
identifiers | result
----------------------------------------------
var var | the code works well
var let | not defined error
let var | has been already declared error
let let | not defined error
所以第三题是:
- 为什么每个人都反对使用
var
,而在这种情况下双重使用var
是代码完美运行的唯一方法?是例外吗?
- How is it possible that line 6 (which hasn't been executed yet) makes line 5 end up with an error?
因为用 let
、const
和 class
声明的绑定范围(松散地,"variables")是整个块,而不仅仅是它们所在的位置重新声明到块的末尾。从代码进入块到执行let
语句之间的时间称为临时死区(TDZ),在此期间绑定存在但未初始化且无法使用以任何方式。即使在代码流中遇到 let foo
之前,块中的 let foo
也会遮挡外部 foo
。
除了范围之外,这个 TDZ 是 var
和 let
之间的最大区别在于 var
创建绑定 并且 初始化它到 undefined
,无论 var
语句在范围内的哪个位置。相反,let
(以及 const
和 class
)创建绑定,但 不 初始化它直到稍后,当 let
(const
, class
) 在逐步执行代码中遇到。您不能使用未初始化的绑定。
- Why does it throw an incorrect error?
没有错。你可以说它措辞不当。 :-) 基本上是说 "you can't use foo
here, it's not initialized." 来自 V8 的当前错误消息(Chrome 中的 JavaScript 引擎、Chromium、Brave、新的基于 Chromium 的 Edge 和 Node.js)在我看来,更清晰:
Uncaught ReferenceError: Cannot access 'foo' before initialization
当您使用 let
声明变量时,它在当前代码块的范围内有效。您的第二个 let foo
声明定义了一个与第一个变量不同的 foo
并且它仅在 if 块中有效。但是,您在定义它之前使用它,因此您会正确地得到它尚未定义的错误。
如果您确实打算有两个不同的 foo
变量,我建议您将它们命名为其他名称(例如 foo1 和 foo2)以避免冲突。然后很明显您在定义变量之前就在使用它。
let foo1 = 2;
console.log(foo1);
if (foo1 === 2){
console.log(foo1);
let foo2 = 1;
}
如果您的意思是第 5 行使用 foo
的第一个实例设置为 2,那么您已经通过 if 代码块中发生的新定义隐藏了它。
如果您的意思是要在第 5 行使用设置为 1 的 foo
,那么您应该将其定义移至使用前。
请注意,使用 var
会产生不同的结果,因为 var
变量的范围比 let
变量的范围更广。请参阅具有此定义的 here:
let allows you to declare variables that are limited to a scope of a block statement, or expression on which it is used, unlike the var keyword, which defines a variable globally, or locally to an entire function regardless of block scope.
为了让它更清楚,我在代码的每个阶段用变量的状态标记了你的代码:
let foo = 2; // foo defined and set to 2
console.log(foo); // foo defined and set to 2
if (foo === 2) // foo defined and set to 2
{ // <-- start of the if-block!
console.log(foo); // foo not defined yet
let foo = 1; // foo defined and set to 1
} // <-- end of if-block!
console.log(foo); // foo defined and set to 2