---
# System prepended metadata

title: const & let
tags: [javascript, ' note']

---

---
tags: javascript, note
---

# const & let

ES6 新增修改了些許功能，讓開發者可以避免掉一些老舊常見的問題，以及加入許多語法糖來增加開發速度!

## var 會遇到的問題

- **重複宣告會直接覆蓋掉**，而不會報錯。

```javascript=
var iLoveYou = false;
```
若干行之後...
```javascript=5201314
var iLoveYou = true;
```

你不會注意到幾萬行之前已經宣告過了，所以造就了一連串的大爆炸、俗成核爆。 (?)
別讓自己的電腦成為核爆家園(?)最好就是使用 let 及 const ，當你重複宣告的時候會直接報錯提醒。

- for loop & if/else 區塊內的變數其實都是屬於**全域變數**

```javascript=
/* for loop */
for (var i = 0; i < 10; i++) {
    console.log(i);  // output -> 1 2 3... 9
}
console.log(i);  // output -> 10

/* if/else */
if (true) {
    var a = 1;
    console.log(a);  // output -> 1
}
console.log(a);  // output -> 1
```

JS 的全域變數跟 CSS 的 !important 一樣，都是需要非常謹慎地使用，能不用就別用。

而如果你多次使用 for loop or if/else 判斷式，你也等於是不斷在全域重複宣告同一個變數的概念。 (相信大家都是按照潛規則命名為 i, j, k 之類的吧 XD)

接著我們來看看古早的解決辦法。

```javascript=
/* 古早味解決法: IIFE，這才是能夠限制 var 作用域緊箍兒 */
(function() {
    /* for loop */
    for (var i = 0; i < 10; i++) {
        console.log(i);  // output -> 1 2 3... 9
    }
    
    /* if/else */
    if (true) {
        var a = 1;
        console.log(a);  // output -> 1
    }
})();

console.log(i);  // output -> is not defined
console.log(a);  // output -> is not defined
```

## 新變數英雄襲來!

```const``` 跟 ```let``` 只有一個地方不同，就是 ```const``` **無法重新賦值**、也因為如此，所以宣告時也就要立刻賦值，否則會報錯!

```const``` 原意為 "常數 (constant)"，意即不可改變之數；
但是對於物件(包含陣列)的內容是可以修改的，單純不能 reassign。

解決的問題

- 避免變數被重複宣告

```javascript
let a = 1;
let a = 'a';

// ouput -> SyntaxError: Identifier 'a' has already been declared
```

- 變數作用域更好限制

```var``` 的作用域是在 function 內
```let```、```const``` 的作用域是在 block 內，也就是 **{}** 內
for loop & if/else 變數也可以限制在範圍內了

```javascript
/* Block */
let a = 1;

{
    let a = 'a';
    
    console.log(a);  // output -> 'a'
}

console.log(a);  // output -> 1
```

```javascript
/* for loop */
for (let i = 0; i < 10; i++) {
    console.log(i);  // output -> 1, 2, 3... 9
}

console.log(i);  // output -> is not defined
```

```javascript
/* if/else */
if (true) {
    let a = 1;
    console.log(a);  // output -> 1
}

console.log(a);  // output -> is not defined
```

## 時戰應用技巧

### 非同步經典考題: for 搭配 setTimeout

```javascript=
for (var i = 0; i < 10; i++) {
    setTimeout(function() {
        console.log(i);
    }, 0);  // 非同步的函式會先被放到 event stack
}

console.log(i);  // output -> 10

/* 機著執行 setTimeout */
// output -> 10, 10, 10... 10
```

因為 ```var``` 的關係 i 成了全域變數，
所以當回頭過來執行事件柱列內容時 i 的值早已變成 10。

而使用 ```let``` 的話則可以解決這個問題，
因為變數包裹在 block 內一起送到事件柱列，
所以雖然非同步也是最後執行，但是變數會被個別保留，
以至於可以顯示出不同的數字，實作如下:

```javascript=
for (let i = 0; i < 10; i++) {
    setTimeout(function() {
        console.log(i);
    }, 0);  // 非同步的函式會先被放到 event stack
}

console.log(i);  // is not defined

/* 機著執行 setTimeout */
// output -> 1, 2, 3... 9
```

### 讓 preventExtensions、seal 和 freeze 更加安全

雖然 preventExtensions、seal 和 freeze 可以限制物件，但如果物件是用 ```var``` 宣告出來的話，其實我可以強制重新 reassign 一個新的物件進去，一切就破解了；
因為物件是 pass by referance，所以我其實是修改變數的 referance，而非修改物件本身。

而如果物件都使用 ```const``` 去宣告，除了可以限制物件作為，更可以避免物件被不小心 reassign，所以更加地安全了! 

## Hoisting 與 TDZ

```javascript
console.log(a);  // output ->  Cannot access 'a' before initialization 
let a = 1;
```

1. ```let``` 一樣會有創造階段。
2. 從創造到實際執行階段會出現 [TDZ](https://eddychang.me/es6-tdz/)，在此區域/時間內無法呼叫、調用變數。
3. ```let``` 與 ```var``` 一樣有創造到執行的概念，但不會預先設定 undefined，而是出現錯誤提示。
4. 文件不會表明這與 ```var``` 的 Hoisting 相同。

:::info
:bulb: 在執行流程一進入作用域建立變數，到變數開始可被存取之間的一段時間，就稱之為 TDZ (Temporal Dead Zone - 暫時性死區)。
:::