# [ES6] 變數宣告 - let、const 該用哪個好? <div class="block"> **本篇會談到** - 變數宣告- `let` - let 與 var 的差異 - let 會存在 block 裡 - let 不會出現在 window 裡面(全域) - let 無法重複宣告同一個變數 - let 的暫時性死區(Temporal Dead Zone TDZ) - 變數宣告- `const` - 不能重新賦值 </div> ## 變數宣告- `let` 宣告可變動的變數,使用 let 代替 var ### let 與 var 的差異 | | var | let | | -------- | -------- | -------- | | 作用域 | var在function上 | let 在block上 | | window 全域物件 |會出現在 window 中| let 不會在 window 上| |宣告|重複宣告不會跳錯|let 不可重複宣告| |hoisting |var 在宣告前取值時,會出現hoisting(提升),得到 undefined|let 有 hoisting 但因為有暫時性死區(TDZ)出現ReferenceError錯誤| :::danger let/const 宣告的變數也是會有提升(hoist)的作用。提升是 JS 語言中對於變數宣告的基本特性,只是因為 TDZ 的作用,並不會像使用 var 來宣告變數,只是會得到undefined而已,現在則是會直接拋出ReferenceError錯誤,而且很明顯的這是一個在執行期間才會出現的錯誤。 ::: ### let 會存在 block 裡 `區域變數`:在區塊內等被 `{ }` 包起來的地方宣告的變數(例如 if、else、for、while) :::info let 屬於 block 作用域 這讓每個宣告的屬性都獨立存在於 block 內,這也是為何 let 比 var 穩定很多的原因 ::: 1. let 在 `大括號` 裏 - `{}` 大括號屬於 block ```javascript= { let a = 1; } console.log(a); // a is not defined // let 的作用域只存在 block 內,不像 var 還能在外層取到值 ``` - [比較: var 在 block 裡](https://hackmd.io/@unayojanni/HyzWz9R0u/%2Fm5NKLfVcROu_iC7DCMDX0w#✐-var-的作用域),var 在大括號內的作用域沒有作用,所以可以取到外面的值 2. let 在 `function` 裏 - `function` 也是 block 所以離開函式無法取值 ```javascript= function fn() { let a = 1; } console.log(a); // a is not defined ``` - let 在作用域裡,取值就必須在大括號中 ```javascript= function fn() { let a = 1; console.log(a); } fn(); // 1 ``` 3. 常見考題 `for` 範例 :::info setTimeout 屬於非同步行為,但因為宣告 let 的作用域會在 大括號中,所以不受到非同步影響。 所以 let 會穩定許多 ::: ```javascript= 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 穩定很多的原因 ``` - 比較:[for 宣告 var](https://hackmd.io/@unayojanni/HyzWz9R0u/%2Fm5NKLfVcROu_iC7DCMDX0w#✐-for-迴圈) ### 全域物件 let 宣告的變數不會出現在 window 裡面 ```javascript= var a = 0; let b = 1; console.log(window) // 上方輸出後打開 window 這個全域物件會看到 a 但不會看到 b ``` ![](https://i.imgur.com/hl85Vyo.png =500x) ### let 無法重複宣告同一個變數 let 不能對已經進行宣告的變數,再一次宣告,但可以用賦予的方式改變值 ```javascript= let a = 0; let a = 1; console.log(a) // Identifier 'a' has already been declared ``` ### let 的暫時性死區(Temporal Dead Zone TDZ) 沒有辦法再宣告 let 之前,去取得值。 const 也一樣。 ```javascript= // 變數的部分 console.log(a) let a = 0; // Cannot access 'a' before initializatio // 你不能在初始化之前獲取他 需在宣告的下一行才執行操作 ``` > [理解ES6中的暫時死區(TDZ)](https://eddychang.me/es6-tdz/) > 當程式的控制流程在新的作用域(module, function 或 block 作用域)進行實體化時,在此作用域中的用 let/const 宣告的變數會先在作用域中被建立出來,但因此時還未進行詞法綁定,也就是對宣告語句進行求值運算,所以是不能被存取的,存取就會拋出錯誤。所以在這執行流程一進入作用域建立變數,到變數開始可被存取之間的一段時間,就稱之為 TDZ(暫時死區)。 ## 變數宣告- `const` ### 不能重新賦值 - 宣告時就要同時賦值,不然會報錯 - 賦值後就不能被更動 - 區塊內宣告,都不會洩漏到全域 ```javascript= const b = 0; b = 1; console.log(b); // Uncaught SyntaxError: Identifier 'b' has already been declared // 因為用 const 宣告的是常數 常數是不能被重新賦值的 ``` ### 修改屬性,可以用 const 宣告 `const` 不能再一次指定值。假設常數的內容(值)是個物件,那麼此物件的內容(物件的參數)是可以更改的。 因為 `物件傳值` 的特性,新的值賦予到變數上,並不會更動記憶體位置。 1. 修改物件中的屬性 ```javascript= const a = { name: 'white' } a.name = 'dark'; console.log(a); // {name:'dark'} ``` 2. f ```javascript= const family = ['dad', 'mom', '小明']; family.forEach((item, key) => { if(item === '小明') { family.splice(key, 1) } }); console.log(family); // ['爸', '媽'] // 屬於修改陣列中的其中一個 // 所以也是傳參考 可以用 const ``` ### 修改整個物件,不可以用 const `物件傳參考` 特性:**`物件`、`陣列`、`函式`**,並不會直接寫值,會建立一個記憶體空間,所以更改位置不可以使用 `const` ```javascript= const a = { name: 'white' } a = { name: 'dark' } // TypeError ``` > 參考: > - [ES6 小筆記】變數宣告 - let、const 哪裡好?跟 var 說掰掰](https://ithelp.ithome.com.tw/articles/10213188) ###### tags: `JS` {%hackmd @unayojanni/H1Qq0uKkK %}