繁体   English   中英

Javascript 中 let 和 var 的奇怪行为

[英]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 = 1var 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 模式对正常无效重新声明的限制放宽时,团队决定(继续)禁止会改变类型( varletconst )的重新声明一个变量,以及 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具有无法修复的奇怪语义,因为它会破坏与现有网站的向后兼容性。 完全忘记关键字会产生更奇怪的行为,尤其是在涉及函数时。

当您定义一个没有varletconst的变量时,它会被输入到全局 scope 中。 尝试这个:

在您选择的 JS 解释器中,执行以下操作:

>>> a = 5
>>> global

你会看到一堆数据,在最底部,你会看到a = 5

现在,输入

>>> var a = 7
>>> global

你会看到同样的东西,但现在 a 是 7。

重启你的解释器并输入

>>> a = 5
>>> let a = 7
>>> global

你会看到a仍然等于5! 这给了我们一个提示,即varlet没有使用相同的 scope。 重新启动您的解释器。

>>> a = 5
>>> let a = 6
>>> var a = 7  // Syntax Error!

现在,试试这个:

>>> var b = 6
>>> var b = 7

注意没有语法错误? 我们发现,如果您在全局级别重新声明全局变量,它们不会引发语法错误。 但是,您不能在全局级别重新声明已在较低级别声明的变量。 letvar下面的 scope 中创建一个变量,它在全局 scope 中声明它。 这就是为什么当你运行var两次时,什么都没有发生,但是当你运行let then var (或let then let )时,你会得到一个语法错误。

但是,正如我们从其他答案中看到的那样,并非所有口译员都如此。 我的本地版本的 Node 与您的结果相同,而其他一些解释器会针对案例 2 抛出语法错误。

由于a = 1 ,案例 2 不会引发异常。

请记住, let变量在其值被评估之前不会被初始化。

var相比, var初始化为undefinedlet变量不同

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM