简体   繁体   English

Javascript 中 let 和 var 的奇怪行为

[英]The strange behavior of let and var in Javascript

case 1情况1

var a  // ===> undefined
let a  // ===> SyntaxError: Identifier 'a' has already been declared

case 2案例2

a = 1  // ===> 1
var a  // ===> undefined
let a  // ===> undefined

Why case 2 does not throw an exception?为什么案例 2 不抛出异常?

V8 developer here. V8 开发人员在这里。

The first thing worth pointing out is that there are differences between regular script execution and "console mode" (aka "REPL mode").首先值得指出的是,常规脚本执行和“控制台模式”(又名“REPL 模式”)之间存在差异。 The former is specified by the JavaScript standard, so all engines should behave the same there;前者由 JavaScript 标准指定,因此所有引擎的行为应该相同; otherwise it's a bug.否则这是一个错误。 The latter is not specified;后者未指定; it should obviously be similar to the former, but due to the interactive usage it turns out that it makes sense to have some minor differences.它显然应该与前者相似,但由于交互式使用,事实证明有一些细微差别是有意义的。

Let's look at a few concrete examples and explain what's going on.让我们看一些具体的例子并解释发生了什么。

(1) Re-declaring a let -variable is invalid in regular script. (1) 在正则脚本中重新声明一个let变量是无效的。 In the DevTools console, that turns out to be annoying though:在 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!!!

So a while ago Chrome made some changes to special-case the behavior around let -redeclarations for the console, which is why you can now see the following difference:因此,不久前,Chrome 对控制台的let -redeclarations 的特殊情况进行了一些更改,这就是为什么您现在可以看到以下差异的原因:

> let a; let a;  // One script: redeclaration error

> let b
> let b  // Separate evaluations: OK, no error.

If you executed the last two lines as part of regular script execution, you'd get the same error as when putting both declaration on the same line in DevTools.如果您将最后两行作为常规脚本执行的一部分执行,您会得到与将两个声明放在 DevTools 中的同一行时相同的错误。

(2) var declarations vs. implicit global variables: there is a small difference between c = 1 and var d = 1 : the former creates a configurable property on the global object, the latter creates a non-configurable property, so while the two are almost equivalent, they can still be distinguished afterwards. (2) var声明 vs. 隐式全局变量: c = 1var d = 1之间存在细微差别:前者在全局 object 上创建可配置属性,后者创建不可配置属性,因此两者几乎等价,之后仍然可以区分。 Now, in regular script execution, var declarations are hoisted, so if you have an explicit declaration following an implicit use of the same variable:现在,在常规脚本执行中, var声明被提升,所以如果你在隐式使用相同变量后有一个显式声明:

e = 1;
var e = 2;

then this gets transformed internally to: var e; e = 1; e = 2然后在内部将其转换为: var e; e = 1; e = 2 var e; e = 1; e = 2 var e; e = 1; e = 2 . var e; e = 1; e = 2 So in this case, you can't observe the configurability difference.所以在这种情况下,您无法观察到可配置性差异。

In console mode, the exact same behavior isn't possible: when you execute e = 1 , the engine can't know yet that you're going to type var e = 2 next.在控制台模式下,完全相同的行为是不可能的:当你执行e = 1时,引擎还不知道你接下来要输入var e = 2 So it creates e as a configurable property of the global object.因此,它将e创建为全局 object 的可配置属性。 When var e follows, it doesn't do anything, because the e property already exists.var e跟随时,它什么也不做,因为e属性已经存在。

(3) let variables vs. global object properties: variables declared with let do not create properties on the global object, they are in their own scope, shadowing the global property. (3) let变量 vs. 全局 object 属性:用let声明的变量不会在全局 object 上创建属性,它们在自己的 scope 中,遮蔽了全局属性。 Here's how you can observe that effect (both in regular script and in the console):以下是观察该效果的方法(在常规脚本和控制台中):

f = 1
let f = 2
console.log(window.f, f)  // prints "1 2"

(4) Limitations to redeclarations in REPL mode: when REPL mode with its loosened restrictions on normally-invalid redeclarations was built for V8, the team decided to (continue to) disallow redeclarations that would change the kind ( var , let , const ) of a variable, as well as redeclaration of consts: (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

(So there's only one difference to regular script execution here, see the comment on that line above.) (所以这里与常规脚本执行只有一个区别,请参阅上面那行的注释。)

Armed with this knowledge, we can now get back to your original question:有了这些知识,我们现在可以回到您最初的问题:

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: Use let for everything, and your code will behave reasonably. TL;DR:对所有事情都使用let ,你的代码会表现得合理。 var has weird semantics that can't be fixed because it would break backwards compatibility with existing websites. var具有无法修复的奇怪语义,因为它会破坏与现有网站的向后兼容性。 Forgetting the keyword entirely gives even weirder behavior, especially once functions are involved.完全忘记关键字会产生更奇怪的行为,尤其是在涉及函数时。

When you define a variable without var , let , or const , it is entered into the global scope.当您定义一个没有varletconst的变量时,它会被输入到全局 scope 中。 Try this:尝试这个:

In the JS interpreter of your choice, do this:在您选择的 JS 解释器中,执行以下操作:

>>> a = 5
>>> global

You'll see a bunch data, and at the very bottom, you'll see a = 5 .你会看到一堆数据,在最底部,你会看到a = 5

Now, enter现在,输入

>>> var a = 7
>>> global

You'll see the same thing, but now a is 7.你会看到同样的东西,但现在 a 是 7。

Restart your interpreter and enter重启你的解释器并输入

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

You'll see a still equals 5!你会看到a仍然等于5! This gives us a hint that var and let aren't using the same scope.这给了我们一个提示,即varlet没有使用相同的 scope。 Restart your interpreter.重新启动您的解释器。

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

Now, try this:现在,试试这个:

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

Notice how there's no syntax error?注意没有语法错误? We've discovered that global variables will not throw syntax errors if you redeclare them at the global level.我们发现,如果您在全局级别重新声明全局变量,它们不会引发语法错误。 However, you cannot redeclare a variable at the global level that is already declared at a lower level.但是,您不能在全局级别重新声明已在较低级别声明的变量。 let creates a variable in a scope below var , which declares it in the global scope. letvar下面的 scope 中创建一个变量,它在全局 scope 中声明它。 That's why when you run var twice, nothing happens, but when you run let then var (or let then let ), you get a syntax error.这就是为什么当你运行var两次时,什么都没有发生,但是当你运行let then var (或let then let )时,你会得到一个语法错误。

However , as we can see from the other answers, this is actually not true of all interpreters.但是,正如我们从其他答案中看到的那样,并非所有口译员都如此。 My local version of Node has the same results you do, while some other interpreters will throw a Syntax Error for case 2.我的本地版本的 Node 与您的结果相同,而其他一些解释器会针对案例 2 抛出语法错误。

Case 2 does not throw an exception because of the a = 1 .由于a = 1 ,案例 2 不会引发异常。

Remember that let variables are not initialized until their value is evaluated.请记住, let变量在其值被评估之前不会被初始化。

In comparison to var , var is initialized with undefined unlike let variablesvar相比, var初始化为undefinedlet变量不同

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

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