Try   HackMD

[ES6] 變數宣告 - let、const 該用哪個好?

本篇會談到

  • 變數宣告- let
    • let 與 var 的差異
    • let 會存在 block 裡
    • let 不會出現在 window 裡面(全域)
    • let 無法重複宣告同一個變數
    • let 的暫時性死區(Temporal Dead Zone TDZ)
  • 變數宣告- const
    • 不能重新賦值

變數宣告- let

宣告可變動的變數,使用 let 代替 var

let 與 var 的差異

var let
作用域 var在function上 let 在block上
window 全域物件 會出現在 window 中 let 不會在 window 上
宣告 重複宣告不會跳錯 let 不可重複宣告
hoisting var 在宣告前取值時,會出現hoisting(提升),得到 undefined let 有 hoisting 但因為有暫時性死區(TDZ)出現ReferenceError錯誤

let/const 宣告的變數也是會有提升(hoist)的作用。提升是 JS 語言中對於變數宣告的基本特性,只是因為 TDZ 的作用,並不會像使用 var 來宣告變數,只是會得到undefined而已,現在則是會直接拋出ReferenceError錯誤,而且很明顯的這是一個在執行期間才會出現的錯誤。

let 會存在 block 裡

區域變數:在區塊內等被 { } 包起來的地方宣告的變數(例如 if、else、for、while)

let 屬於 block 作用域 這讓每個宣告的屬性都獨立存在於 block 內,這也是為何 let 比 var 穩定很多的原因

  1. let 在 大括號

    • {} 大括號屬於 block
    ​​​​{ ​​​​ let a = 1; ​​​​} ​​​​console.log(a); ​​​​// a is not defined ​​​​// let 的作用域只存在 block 內,不像 var 還能在外層取到值
  2. let 在 function

    • function 也是 block 所以離開函式無法取值
    ​​​​function fn() { ​​​​ let a = 1; ​​​​} ​​​​console.log(a); ​​​​// a is not defined
    • let 在作用域裡,取值就必須在大括號中
    ​​​​function fn() { ​​​​ let a = 1; ​​​​ console.log(a); ​​​​} ​​​​fn(); ​​​​// 1
  3. 常見考題 for 範例

    setTimeout 屬於非同步行為,但因為宣告 let 的作用域會在 大括號中,所以不受到非同步影響。 所以 let 會穩定許多

    ​​​​for (let i = 0; i < 10; i++) { ​​​​ console.log(i); ​​​​ // 第一步驟: 輸出 0~9 ​​​​ setTimeout(() => { ​​​​ console.log(i); ​​​​ // 第三步驟:輸出 0~9 ​​​​ // setTimeout 屬於非同步行為 ​​​​ // JS 默認在所有事件結束後才會執行非同步行為 ​​​​ // !但是 let 的作用域會存在在大括號內,所以不會受到非同步影響 ​​​​ }, 0); ​​​​} ​​​​console.log(i); ​​​​// 第二步驟: 錯誤:i is not defined ​​​​// for 迴圈裡使用 var 時 setTimeout 都是輸出 10 ​​​​// 但 let 卻可以正常輸出 0~9 ​​​​// 原因就是 let 屬於 block 作用域 這讓每個 i 都獨立存在於 block 內 ​​​​// 這也是為何 let 比 var 穩定很多的原因

全域物件

let 宣告的變數不會出現在 window 裡面

var a = 0; let b = 1; console.log(window) // 上方輸出後打開 window 這個全域物件會看到 a 但不會看到 b

let 無法重複宣告同一個變數

let 不能對已經進行宣告的變數,再一次宣告,但可以用賦予的方式改變值

let a = 0; let a = 1; console.log(a) // Identifier 'a' has already been declared

let 的暫時性死區(Temporal Dead Zone TDZ)

沒有辦法再宣告 let 之前,去取得值。
const 也一樣。

// 變數的部分 console.log(a) let a = 0; // Cannot access 'a' before initializatio // 你不能在初始化之前獲取他 需在宣告的下一行才執行操作

理解ES6中的暫時死區(TDZ)
當程式的控制流程在新的作用域(module, function 或 block 作用域)進行實體化時,在此作用域中的用 let/const 宣告的變數會先在作用域中被建立出來,但因此時還未進行詞法綁定,也就是對宣告語句進行求值運算,所以是不能被存取的,存取就會拋出錯誤。所以在這執行流程一進入作用域建立變數,到變數開始可被存取之間的一段時間,就稱之為 TDZ(暫時死區)。

變數宣告- const

不能重新賦值

  • 宣告時就要同時賦值,不然會報錯
  • 賦值後就不能被更動
  • 區塊內宣告,都不會洩漏到全域
const b = 0; b = 1; console.log(b); // Uncaught SyntaxError: Identifier 'b' has already been declared // 因為用 const 宣告的是常數 常數是不能被重新賦值的

修改屬性,可以用 const 宣告

const 不能再一次指定值。假設常數的內容(值)是個物件,那麼此物件的內容(物件的參數)是可以更改的。

因為 物件傳值 的特性,新的值賦予到變數上,並不會更動記憶體位置。

  1. 修改物件中的屬性
const a = { name: 'white' } a.name = 'dark'; console.log(a); // {name:'dark'}
  1. f
const family = ['dad', 'mom', '小明']; family.forEach((item, key) => { if(item === '小明') { family.splice(key, 1) } }); console.log(family); // ['爸', '媽'] // 屬於修改陣列中的其中一個 // 所以也是傳參考 可以用 const

修改整個物件,不可以用 const

物件傳參考 特性:物件陣列函式,並不會直接寫值,會建立一個記憶體空間,所以更改位置不可以使用 const

const a = { name: 'white' } a = { name: 'dark' } // TypeError

參考:

tags: JS

最後,親愛的大家!我需要你的大聲鼓勵 ٩(⚙ᴗ⚙)۶

如果覺得這篇文章對你有幫助,請給我個一個小小的鼓勵 ❤ 讓我知道,這會成為我寫下去很大的動力。
對了,我還有其他文章,如果有興趣也來逛逛吧!
(文章中如有覺得不妥之處、錯誤內容,也可以透過聯絡我,我會儘速改善,感謝!)

☞ YoJanni 珍妮 2021 正在設計轉職前端的路上,希望大家在學習的路上能夠一起成長
☞ 聯絡我