# 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 %}