[英]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.