ES6 let, var 差異比較
剛接觸 ES6 時,最容易發現的差異是以前熟悉的變數宣告關鍵字 var
,全部都被替換成了 let
,兩者之間到底有什麼差別呢?在這篇文章會將彼此間的差異做一個整理。
1. 新的作用域
在 ES6 之前,想要宣告區域變數 (local variable),唯一的方法只有開一個 function 在裡面宣告。
var i = 99;
for(var i = 0; i < 5; i++){
// do something...
}console.log(i); // 5
在上面的程式碼中,預期應該要印出99,然而 var
只要不在 function 內就具有全局作用域,整個程式碼會共同一個 i ,重複宣告會蓋過之前的值,並且不會有語法錯誤。
然而現今 javascript 早已不限於簡單的網頁腳本,這樣的作用域規範顯得不敷使用,對於物件導向的設計也礙手礙腳。
有鑑於此,在 ES6 中引入了新的關鍵字 let
,和新的作用域 “區塊(block)”,可以為乾淨的大括號 (如下例),或帶有 label (for、if …)。
{
let message = "U can't C me!";
console.log(message); // U can't C me!
}console.log(message); // ReferenceError: message is not defined
此例中變數 message 的作用域只在大括號的範圍內,若將let
改成var
,在 log 裡則會看到兩行相同的字串。
2. 不同的 lifecycle
考慮以下程式碼:
{
console.log(a); // undefined
var a = 1;
}{
console.log(b); // ReferenceError: b is not defined
let b = 1;
}
造成此結果的原因可分為兩步驟,首先,由於 javascript 在執行時會將所有的變數和函式宣告 ”提升 (hoist)” 至 block 的最頂端 (這也是為什麼無論在哪宣告 function 都能正常使用的原因),也就是說這段程式碼同義於:
{
var a;
console.log(a); // undefined
a = 1;
}{
let b;
console.log(b); // ReferenceError: b is not defined
b = 1;
}
接下來,由於兩者的 lifecycle 不同,使用 let
宣告的變數在未賦值時被呼叫時會 throw ReferenceError,而使用 var
則會直接印出 undefined。關於兩者詳細的 lifecycle 差別可以參考 這篇文章。
3. 迴圈內重新綁定 (bind)
先看範例:
for (var i = 0; i < 5; i++) {
setTimeout(function(){
console.log(`Hi I'm No.${i}!`); // 印出 'Hi I'm No.5!' 五次
}, 100);
}
在此例中,每個閉包 (closure) 共享了同一個 i
,造成在 0.1 秒過後要印出第一個值時,迴圈早已跑完,而此時的i
為 5,並不再更動。將上例中的var
改為let
,則因為每個閉包都會綁定不同的i
,讓五兄弟終於可以團圓了。附帶一提,由於每次都需要綁定,在迴圈迭帶次數大時會感受到彼此間的速度差異。
4. 重複宣告會造成語法錯誤
在過去使用 var
時,若重複宣告變數,則會無視後者的宣告,僅重新賦值給它。但使用 let
重複宣告則會 throw SyntaxError,這項特性有助於檢查程式中的小 bug。
let x = 1;
let x = 2; // SyntaxError: Identifier 'x' has already been declared
5. 不是全域個體 (global object) 的屬性
在瀏覽器運行的 javascript 之全域個體一律為 window
,然而在全域範圍使用let
宣告,實際上是存在於一個看不見的作用域中的區域變數。
var x = 1;
console.log(window.x); // 1let y = 2;
console.log(window.y) // undefined
該棄用 var 嗎?
我的回答是:是。
這也是網路上大多人的回答,然而仍然存在一些擁護 var
的人,理由包括相容性 (現今的瀏覽器幾乎都能支援 let,可以參考 Can I Use)、修改舊有程式碼可能出錯等,有興趣可以參考這個知乎討論串。
參考資料