簡體   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