[英]The strange behavior of let and var in Javascript
情况1
var a // ===> undefined
let a // ===> SyntaxError: Identifier 'a' has already been declared
案例2
a = 1 // ===> 1
var a // ===> undefined
let a // ===> undefined
为什么案例 2 不抛出异常?
V8 开发人员在这里。
首先值得指出的是,常规脚本执行和“控制台模式”(又名“REPL 模式”)之间存在差异。 前者由 JavaScript 标准指定,因此所有引擎的行为应该相同; 否则这是一个错误。 后者未指定; 它显然应该与前者相似,但由于交互式使用,事实证明有一些细微差别是有意义的。
让我们看一些具体的例子并解释发生了什么。
(1) 在正则脚本中重新声明一个let
变量是无效的。 在 DevTools 控制台中,这很烦人:
> let a = 42j // typo: I meant "42"
< Uncaught SyntaxError: Invalid or unexpected token
> let a = 42 // Let's try that again
< Uncaught SyntaxError: 'a' has already been declared
// Aaaargh!!!
因此,不久前,Chrome 对控制台的let
-redeclarations 的特殊情况进行了一些更改,这就是为什么您现在可以看到以下差异的原因:
> let a; let a; // One script: redeclaration error
> let b
> let b // Separate evaluations: OK, no error.
如果您将最后两行作为常规脚本执行的一部分执行,您会得到与将两个声明放在 DevTools 中的同一行时相同的错误。
(2) var
声明 vs. 隐式全局变量: c = 1
和var d = 1
之间存在细微差别:前者在全局 object 上创建可配置属性,后者创建不可配置属性,因此两者几乎等价,之后仍然可以区分。 现在,在常规脚本执行中, var
声明被提升,所以如果你在隐式使用相同变量后有一个显式声明:
e = 1;
var e = 2;
然后在内部将其转换为: var e; e = 1; e = 2
var e; e = 1; e = 2
var e; e = 1; e = 2
。 所以在这种情况下,您无法观察到可配置性差异。
在控制台模式下,完全相同的行为是不可能的:当你执行e = 1
时,引擎还不知道你接下来要输入var e = 2
。 因此,它将e
创建为全局 object 的可配置属性。 当var e
跟随时,它什么也不做,因为e
属性已经存在。
(3) let
变量 vs. 全局 object 属性:用let
声明的变量不会在全局 object 上创建属性,它们在自己的 scope 中,遮蔽了全局属性。 以下是观察该效果的方法(在常规脚本和控制台中):
f = 1
let f = 2
console.log(window.f, f) // prints "1 2"
(4) REPL 模式中重新声明的限制:当 REPL 模式对正常无效重新声明的限制放宽时,团队决定(继续)禁止会改变类型( var
、 let
、 const
)的重新声明一个变量,以及 consts 的重新声明:
> var g
> var g // OK
> let g // error: already declared
> const g = 1 // error: already declared
> let h
> let h // OK (REPL-mode special case)
> var h // error: already declared
> const h = 1 // error: already declared
> const i = 1
> const i = 2 // error: already declared
> var i // error: already declared
> let i // error: already declared
(所以这里与常规脚本执行只有一个区别,请参阅上面那行的注释。)
有了这些知识,我们现在可以回到您最初的问题:
var a // Creates a variable of `var` kind.
let a // Redeclaration -> error
a = 1 // Creates global object property.
var a // Does nothing, because `a` already exists.
let a // Creates a variable of `let` kind, shadowing global property.
TL;DR:对所有事情都使用let
,你的代码会表现得合理。 var
具有无法修复的奇怪语义,因为它会破坏与现有网站的向后兼容性。 完全忘记关键字会产生更奇怪的行为,尤其是在涉及函数时。
当您定义一个没有var
、 let
或const
的变量时,它会被输入到全局 scope 中。 尝试这个:
在您选择的 JS 解释器中,执行以下操作:
>>> a = 5
>>> global
你会看到一堆数据,在最底部,你会看到a = 5
。
现在,输入
>>> var a = 7
>>> global
你会看到同样的东西,但现在 a 是 7。
重启你的解释器并输入
>>> a = 5
>>> let a = 7
>>> global
你会看到a
仍然等于5! 这给了我们一个提示,即var
和let
没有使用相同的 scope。 重新启动您的解释器。
>>> a = 5
>>> let a = 6
>>> var a = 7 // Syntax Error!
现在,试试这个:
>>> var b = 6
>>> var b = 7
注意没有语法错误? 我们发现,如果您在全局级别重新声明全局变量,它们不会引发语法错误。 但是,您不能在全局级别重新声明已在较低级别声明的变量。 let
在var
下面的 scope 中创建一个变量,它在全局 scope 中声明它。 这就是为什么当你运行var
两次时,什么都没有发生,但是当你运行let
then var
(或let
then let
)时,你会得到一个语法错误。
但是,正如我们从其他答案中看到的那样,并非所有口译员都如此。 我的本地版本的 Node 与您的结果相同,而其他一些解释器会针对案例 2 抛出语法错误。
由于a = 1
,案例 2 不会引发异常。
请记住, let
变量在其值被评估之前不会被初始化。
与var
相比, var
初始化为undefined
与let
变量不同
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.