---
# System prepended metadata

title: JavaScript 變數教學｜var / let / const 宣告差異、作用域、hoisting 一次看懂
tags: [前端養成, const, var, let, hoisting, JavaScript, 作用域, 常數, 白話解釋, 變數, 新手筆記]

---

# JavaScript | 🅿️ 停車位學變數：一次搞懂 var／let／const

JavaScript 第一關是「宣告」這件超級基礎的且重要的「 **變數 var、let / 常數 const** 」

用生活上的停車空間，來理解最基礎也最容易翻車的主題（ 是的，主包最近在練習倒車入庫 ）
🎯 **宣告（ Declarations ）- 變數（ variable ）／常數（ constant ）** 

深入理解後，就不會找不到車位啦！
那麼，我們就開始吧 ❤️‍🔥

![JavaScript | 變數常數三頭龍性格表](https://hackmd.io/_uploads/ByYZY2zhgl.png)

## 🐉 變數 VS. 常數 
變數和常數都是保存數據的一個容器，讓電腦變得有記憶，接著就有了定義變數與並且賦予值

### 🧳 變數（ variable ) - let / var
可以讀取並且允許變數的值再次重新賦值，想變就變！

🥡 生活上對應ㄉ例子 → 家樂福（ let / var ）ㄉ編號 06 停車位（ 變數 ）可以停瑪莎拉帝（ 賦予值 ）

####  ㊙️ let vs. var 差異一眼看懂

| 定義變數 | 作用域     |      沒宣告就呼叫變數      | 重複定義變數 | 版本 |
|:-------- | ---------- |:--------------------------:|:------------:| ---- |
| let      | 區塊作用域 | ReferenceError ( 🔴 明顯 ) |      ❌      | ES6  |
| var      | 全域作用域 |         undefined          |     ⭕️      | ES5  |

###### 🕸️ 實務應用的地方：User 輸入名字、是否單身、電話 →（ 記下來後要打電話打到你答應當我男友為止❤️‍🔥 ）



#### `let` →  區塊作用域（ ES6 規範 ）
```javascript!
// 宣告變數
let 變數名稱 A; // 開一個變數的新容器，之後要記憶下來 〠

// 將變數賦予值 → 後賦值（初始）
變數名稱 A = 值; 

// 簡潔ㄉ寫法 → 宣告即賦值
let 變數名稱 B = 值; 

↑ 以上ㄉ兩種寫法都是可以ㄉ

---
    
// 會噴錯ㄉ寫法 不可重複宣告變數 ❌ 
let 變數名稱 A = 值 A;
let 變數名稱 A = 值 B;

// 允許重新賦予值 ⭕️
let 變數名稱 A = 值 A;
變數名稱 A = 值 B;
```

🅿️ 停車場ㄉ例子我們來試試看吧 → 
```javascript!
// 家樂福有一格 lucky 06 車位 大家都喜歡停
let lucky06; // 把 車位 在記憶體中畫一格空間出來（ 💎 宣告有 lucky06 這個車位 ）

// 一早 瑪莎辣地888 停進來ㄌ
lucky06 = '瑪莎辣地888';

// 後來 頭優塔666 接著停進來，車位只有一個，所以 頭優塔666 現在停在 lucky06
lucky06 = '頭優塔666';
console.log(lucky06); 
```
![image](https://hackmd.io/_uploads/BJavADBhgl.png)

```javascript!
// 家樂福經理覺得 既然大家都喜歡 lucky 06 車位 那我再多畫一格一樣ㄉ，人進來，家樂福發大財 🤑
let lucky06 = '瑪莎辣地888';
let lucky06 = '頭優塔666';

console.log(lucky06); 
// 這樣會發生什麼事ㄋ？
```
答案是地主生氣ㄌ，這樣他不知道要怎麼收停車費 → 不可以重複宣告ㄛ
![image](https://hackmd.io/_uploads/SJpwy_S3xg.png)
🔴 Uncaught SyntaxError: Identifier 'lucky06' has already been declared →（ 中文 ）常見錯誤：lucky06 已經被宣告

♛ 英文小教室
let  /lɛt/  verb. ☞ 讓、給
`let 變數名稱 = 值;` → ( 白話文 ) 讓 變數名稱 賦予 值; 

Easy不Easy啊？ City不City啊？
![image](https://hackmd.io/_uploads/HJnCUsWnlg.png)

####  `var` →  函式作用域（ ES5 規範 ）
在 ES6 以前，只有全域作用域（ global scope ）與函式作用域（ function scope ）
引入 let/const 的 ES6 版本才新增了區塊作用域（ block scope ) → `{}`



<!-- 研究了很久，發現最主要的差異就是 **提升 ( hoisting )** → 也就是 ES6 版本 let / const 所沒有的
var 的提升有兩種 - 變數提升 函式提升 -->

```javascript!
// 可以重複宣告 var
var myBoyFriends = '年下楊祐寧';
var myBoyFriends = '台中金宣虎'; 

console.log(myBoyFriends);  // 允許重宣告（ 直接覆蓋 ）
```
![image](https://hackmd.io/_uploads/HyDlJVunxl.png)
↑ 雖然台中金宣虎直接幹掉年下楊祐寧ㄌ，但這樣不奇怪ㄇ...

兩ㄍ同時都是我男友ㄉ話（ 宣告ㄌ兩次 myBoyFriends ），很讚很省時但是很危啊！！！
這樣默默無縫蓋掉，不清不楚ㄉ 萬一以後 '年下楊祐寧' 越想越不對勁說我是壞女人怎麼辦！！ 

###### ~~好女人得到名聲，壞女人得到一切 ( 許妍上身 www )~~
![image](https://hackmd.io/_uploads/B1biyvt2xl.png)
###### 💁🏻‍♀️ 世俗上我們要做個好人，乾乾淨淨地重新賦值 → 宣告一個男友，要換就重新賦值

🔫 Qusetion 01：
who 是可以進行重新賦值，並且重複宣告會馬上 ReferenceError 🔴 明顯 ~~告訴你這樣太壞~~ 報錯的呢？  

Answer 01 → `let`

✍️ 來寫一次 modern ㄉ拔
```javascript!
// 〠 現代主流寫法
let myBoyFriends = '年下楊祐寧';

// 乾乾淨淨重新賦值
myBoyFriends = '台中金宣虎'; 

console.log(myBoyFriends); 
```

#### 🤵🏻🕺🏻 야레야레（呀咧呀咧）攔不住的 var~ 
```javascript!
{
    //一般來說 小島上的事情就就留在小島上 塊內的事情就留在塊內 but...
    var missV = '李安娜'
}

console.log(missV); // 猜猜看叫得到嗎 🤖
```
![image](https://hackmd.io/_uploads/rkBn_y62xx.png)

素ㄉ，叫得到因為 hoisting 把變數的宣告提升到作用域頂端

###### 🦉 hoisting ( 提升 ) 簡單來說是把變數提升到最上方，並且不帶賦值

![image](https://hackmd.io/_uploads/S1i2aYmTex.png)


嗯...只是提升到作用域最上方而已，好像也沒什麼嘛 🤔
接下來ㄉ例子可以明確直觀感受到 var 失控之處 （ 提升 + 函式 + 默默覆蓋不報錯💥 ）
![1759912010635](https://hackmd.io/_uploads/HJtsWimpxg.png)
###### let / const 在宣告時就初始化，有錯就 ReferenceError！

#### var + 函式 = Boom💥
前面可能感受不到 var 危險之處，甚至還有點迷人（誒？
現在我們來試著寫函式感受看看什麼叫做炸彈ㄅ 🤡
```javascript!
// ⛰️勞資蜀道山 去洗碗
function washDishes(){
    for(var i = 1; i<4; i++){
        console.log(`勞資蜀道山 去洗碗 ${i} 😡`); // 🅐 第一個 console.log(${i})
        
        // 那如果等洗完碗增一個任務呢？
        setTimeout(() =>{
            console.log(`任務 ${i} 請立即執行 👹`); // 🅑 第二個 console.log(${i})
        },i*1000)
    } 
}

washDishes();
```
🔫 Qusetion 02：🅐 🅑 會印出一樣的嗎？
```javascript!
這時候我們的預期

// 🅐 第一個 console.log(${i})
for(var i = 1; i<4; i++){
    console.log(`勞資蜀道山 去洗碗 ${i} 😡`);
} 
// 🅑 第二個 console.log(${i})
setTimeout(() =>{
    console.log(`任務 ${i} 請立即執行 👹`); 
},i*1000)

都一樣是同樣的 ${i} 肯定都會一樣吧
```
實際上的運行結果 ↓ 
![image](https://hackmd.io/_uploads/BJoag6Vagx.png)
🅐 第一個 console.log(${i}) 正確印出
勞資蜀道山 去洗碗 1 😡
勞資蜀道山 去洗碗 2 😡
勞資蜀道山 去洗碗 3 😡

🅑 第二個 console.log(${i}) 則印出
任務 4 請立即執行 👹
任務 4 請立即執行 👹
任務 4 請立即執行 👹

![下載 (1)](https://hackmd.io/_uploads/rkjiTuJ1-l.jpg) 
###### ~~等等...這我不要吃~~ 

💩 為什麼會這樣呢？我們來看看圖解
![image](https://hackmd.io/_uploads/ryV_zxHTex.png)

這就是 var 失控之處 

👨‍🚒 改成 let 試試看吧
```javascript!
// ⛰️勞資蜀道山 去洗碗
function washDishes(){
    for(let i = 1; i<4; i++){
        console.log(`勞資蜀道山 去洗碗 ${i} 😡`); // 🅐 第一個 console.log(${i})
        
        // 那如果等洗完碗增一個任務呢？
        setTimeout(() =>{
            console.log(`任務 ${i} 請立即執行 👹`); // 🅑 第二個 console.log(${i})
        },i*1000)
    } 
}

washDishes();
```
![image](https://hackmd.io/_uploads/S1vABeBpex.png)
這樣是不是很棒！

因為 let 有 區塊作用域的特性
他不會隨隨便便外洩到區塊外面、又冒冒失失頂替別人 😒



接下來要說更嚴格的 const 
加油 快看完了（ 此刻主包血條剩下 20% 🤡 ）
![下載](https://hackmd.io/_uploads/HJKV6eSplg.jpg)

---

### 💍 常數（constant)
和 ` let `  是同一梯，一樣是區塊作用域
唯一不同的是只能讀取，不允許變數的值再次重新賦值，而且必須當下賦予值

| 定義變數 |   作用域   |      沒宣告就呼叫變數      | 再重複定義變數 | 版本 |
| -------- |:----------:|:--------------------------:|:--------------:| ---- |
| const    | 區塊作用域 | ReferenceError ( 🔴 明顯 ) |       ❌       | ES6  |


```javascript!
const 常數; ❌ 

// 正確寫法：當下給予值 ⭕️
const 常數 = 常數的值;
```

const 就像行照與車主一樣，綁定就不可以換人啦！
換人的話會出現 Uncaught TypeError 🚗
```javascript!
// jimnyㄉ車主指向李安娜
const jimnyDriver = '李安娜';

// 今天借孫小美開可以嗎？
jimnyDriver = '孫小美';
```
![image](https://hackmd.io/_uploads/rkzAQ7p0el.png)

是的翻車ㄌ，車主請負責連帶責任（這比學 code 還重要！）

🔴 Uncaught TypeError: Assignment to constant variable.
→（ 中文 ）型別錯誤：嘗試對常數重新賦值

#### 🚗 const 可以改變指向的內容（物件、陣列裡面的內容）
```javascript!
const liannaCar = {model: 'suzuki-jimny', color: 'jungle green'}

// 可以改顏色嗎？
liannaCar.color = 'ivory white';
console.log(liannaCar);
```
![image](https://hackmd.io/_uploads/S1tGBQTCxe.png)
成功把 jungle green 換成 ivory white ㄌ！

```javascript!
const liannaCar = {model: 'suzuki-jimny', color: 'jungle green'}

// 那我今天想大換特換車種呢？
liannaCar = {model: 'suzuki-ignis'}
console.log(liannaCar);
```
![image](https://hackmd.io/_uploads/HJWM8QTCeg.png)
🔴 Uncaught TypeError: Assignment to constant variable.
不可以重新指向（賦值）！

---
### 參考資料
{%preview https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Guide/Grammar_and_types %}