ECMAScript6 入門:var、let、const 差異
過去常用的 var、window
之前寫 JavaScript 總是用 var
宣告變數,但使用 var
宣告的變數,會汙染全域變數。
舉例:
var a = 1;
console.log(a);
for(var i=0; i<3; i++){
console.log(i);
}
我們可以使用「開發人員工具」輸入 window
去搜尋,會發現最上方出現 a 這個全域變數。
而在 ES6 推出後,有了「區塊域」的概念與 let
的寫法,就能避免掉使用 var
會汙染全域變數的副作用了!
ES6 優缺點概述
- 優點
- 解決 ES5 的 BUG 與不便之處
- 盡量避免汙染全域變數:維護性較高、不會污染其他開發者
- 小問題
- 舊版瀏覽器之問題:使用 Babel + Gulp 可解決
ES6 - let 的特性
let
與 const
用來宣告區塊裡的變數,即「區域變數」,而所謂的「區塊」就是指這個 { 大括號 } 裡面的東西。
我們直接舉個例子來瞭解 let
的特性:
var a = 0; // 這個 a 是全域變數
function changeA(){
let a = 0; // 這個 a 是區域變數
a = 1;
console.log(a); // 結果為 1
}
changeA();
console.log(a); // 結果為 0
上述例子中,呈現在 console 的結果為 1 與 0。
為什麼?
首先,第五行 console.log(a)
的結果為 1,因為 a 在第四行的時候,被重新賦予值為 1。
那第二個 console.log(a)
的答案怎麼不是 1 呢?
因為 function changeA(){...}
裡面的 a 只會存活在那個區塊 { } 裡面,所以 function changeA(){...}
裡面的 a = 1
並沒有變更到外面的 a。
因此第二個 console.log(a)
就會回傳 0 的結果。
註:ES6 建議 JavaScript 都寫成 "函數式"
盡量以函式呼叫的方式去做設計,不要使用到var
let 的 for 用法
我們透過 let
使用於 for
迴圈的時候常見的小問題,來進一步解釋 let
的特性。
下方程式碼當中,我們希望上方的列表被點擊時,會跳出 alert
來通知我們是點到哪一個列表。
因此,我們先監聽 li
並加上 JavaScript 的 click
事件:
<!-- HTML -->
<ul class="list">
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
// Javascript
const listLength = document.querySelectorAll('.list li').length;
for(var i=0; i<listLength; i++){
document.querySelectorAll('.list li')[i].addEventListener('click',function(){
alert(i+1);
})
}
完成之後,雖然點擊 li
確實會彈出 alert
,但是我們卻發現不管點擊的是哪一個 li
,彈跳出來的 alert
的內容都是 4。
這就是因為它的值被「全域變數」所影響。
當 for
迴圈跑完之後,i 的值已經等於 i++
完的結果,汙染了全域變數。
改善方式:使用 let
const listLength = document.querySelectorAll('.list li').length;
for(let i=0; i<listLength; i++){
document.querySelectorAll('.list li')[i].addEventListener('click',function(){
alert(i+1); // 用 let 的話,會在大括號區塊內重新綁定
})
}
重點就在於 for(let i=0; i<listLength; i++){ }
右邊的大括號區塊。
因為 let
的功能就是在 for
執行「每一次」的時候,裡面的 i 都可以存活在個別的大括號作用域裡面。
就是因為這樣,彼此執行的內容才不會像是使用全域變數一樣,會個別干擾!
以白話一點的方式說明:let i = 0
時,i 存活在 i 為 0 的 { }
作用域裡面,執行 function (){ alert(i+1)}
,變成 alert(1)
。let i = 1
時,i 存活在 i 為 1 的 { }
作用域裡面,執行 function (){ alert(i+1)}
,變成 alert(2)
。
... 之後以此類推。
ES 6 - const 的特性
const
是唯讀變數(不能去做修改),常用在一些不能被變更的變數。
例如: url 網址、圓周率 ( PI = 3.14159 )。
不過有個例外,就是 { 物件 } 跟 [ 陣列 ] 還是會被變更,這方面可以用 freeze()
方法解決。
Object.freeze()
freeze()
的功能顧名思義,就是「凍結」一個物件,用於防止物件新增屬性,或是防止原有的屬性被刪除。
const obj = {
url: 'https://sealman.com'
}
Object.freeze(obj); // 使用 freeze 就不能修正了
obj.url = '30';
console.log(obj.url); // 30 -> https://sealman.com
然而,在哪些情況下我們會要使用 freeze()
呢?
通常是有些預設的東西不想被干擾或更改的時候,可以使用 const
與 freeze
(物件或陣列)的特性!
let 與 const 的注意事項
Hoisting (向上提升)
什麼是 Hoisting ?
我們舉個例子來解釋吧:
// var a; // 相當於有這一行存在
console.log(a); // undefined
var a = 1;
console.log(a); // 1
上方程式碼中,第一次的 console.log(a)
為何不是顯示找不到 a 這個變數,而是顯示 undefined
呢?
這是因為 JavaScript 在編譯時,預設會將 var
做「向上提升」,當建立變數或函式時,會自動提升到最上方。
但是,這裡如果將 var
改為 let
,變成 let a = 1;
的話,第一次的 console.log(a)
就會抓不到值囉!
console.log(a); // 抓不到值
let a = 1;
// const a = 1;
console.log(a); // 1
整理:
var
有向上提升的特性,let
跟const
沒有
在同個區塊 { } 上不能重複命名
var
可以重新賦予其值var a = 1; var a = 2; // var 可以重新賦予
let
與const
不能重新賦予值let a = 1; let a = 2; // a 已被賦予值 const b = 1; const b = 2; // b 已被賦予值
const 與 let 不會在全域變數 (window)裡面
這裡可以使用開發人員工具,在 console 輸入 window
檢查。
let a = 1;
const b = 1;
var c = 1;
我們會發現 a 與 b 都不會出現在 window
裡,但是 c 會出現在 window
裡,這是因為 c 是全域變數。
這邊就能回到我們一開始所說的,使用 let
與 const
能避免掉使用 var
會汙染全域變數的副作用囉!
總結
在 ES6 推出後,寫 JavaScript 時可以多使用 let
與 const
,少一點使用 var
。
這麼做除了可以避免使用 var
可能出現的錯誤,也能增加程式碼的可讀性,像是閱讀程式碼的人看到使用 const
所宣告的變數,就會知道這個變數是不能做改變的。