`#JavaScript` `#六角前端課程` `#學習筆記` `#骨力走傱`
## 什麼是 JavaScript?
### 前端三大程式語言
* HTML:網頁結構
* CSS:網頁樣式
* JavaScript:資料互動
### 哪裡可以運行?
* 瀏覽器
* Node.js
### 如何在編輯器建立環境?
1. 建立 HTML、JS 檔。
2. 在 HTML `</body>` 結尾標籤前引入 JS 檔。
```html=
<script src="./all.js"></script>
```
3. 切回 JS,輸入 `console.log("Hello world!!")`,查看開發者工具是否有跳出字串,若有字串,表示引入成功。
#### 為何要在 `</body>` 前引入 JS 檔?
* 確保 JS 能順利操作 DOM 節點,因此等待網頁結構與樣式渲染完畢後再引入,是比較好的作法。
* [筆記|第五堂:開發思維與程式邏輯整合](/RtGsA52CTWywMNeqXKuxLg)
## 什麼是變數(Variable)與值(Value)?
### 說明
- 變數:具備名稱,儲存資料用的記憶體空間。
- 值:儲存於記憶體空間的資料。
### 範例
| 變數 | 值 |
| -------- | --- |
| 香蕉數量 | 3 |
| 蘋果數量 | 5 |
## 什麼是宣告變數?
### 說明
告訴程式碼開啟一個記憶體空間,並告知其空間名稱與存放的資料為何。
```javascript=
let A = "apple";
```
- `let`:宣告的方法。
- `A`:變數。
- `=`:賦予。
- `"apple"`:值。
- `;`:中斷程式碼。
> `"apple"` 賦予在名稱為 `A` 的變數上。
> (若要翻譯成自然語言,通常會從右往左讀。)
## 宣告的種類
### let
```javascript=
let milkPrice = 10;
```
```javascript=
milkPrice = 20;
console.log(milkPrice); // 20
```
* 不可重複宣告相同的變數名稱。
* 可重新賦值。
* 宣告後,可以不賦值。
### const
```javascript=
const sunNum = 1;
```
```javascript=
sunNum = 2;
Uncaught TypeError: Assignment to constant variable. // 無法修改常數
```
* 又稱常數、唯讀變數。
* 不可重複宣告相同的變數名稱。
* 不可重新賦值。
* 宣告後,一定要賦值。
* 不常更動或不希望輕易被修改的值,通常會使用 `const` 宣告。
### var
```javascript=
var carColor = "red";
```
```javascript=
carColor = "blue";
console.log(carColor); // blue
```
* 可重複宣告相同的變數名稱。
* 可重新賦值。
* 宣告後,可以不賦值。
* 較不嚴謹,故容易汙染到其他資料,不推薦使用。
### 比較
| | let | const | var |
| ---------- | --- | ----- | --- |
| 重複宣告 | ❌ | ❌ | ✔️ |
| 重新賦值 | ✔️ | ❌ | ✔️ |
| 可以不賦值 | ✔️ | ❌ | ✔️ |
## 變數命名
### 命名規則
1. 不可使用數字開頭。
2. 有區分大、小寫(`catNum`、`catnum` 為不同的記憶體空間)。
3. 可使用中文命名。
4. 可使用特殊符號開頭(`_`、`$`)。
5. 不可使用[保留字(關鍵字)](https://www.w3schools.com/Js/js_reserved.asp)命名。
### 命名習慣
#### 駝峰式大小寫
當變數名稱和函式名稱是多個單字連結在一起時,利用「駝峰式大小寫」來表示,以增加變數和函式的可讀性。
| | 小駝峰 | 大駝峰 |
| ---- | -------------------------------------------------- | ---------------------------------------------------------- |
| 說明 | 第一個單字以小寫字母開始;第二個單字的首字母大寫。 | 每一個單字的首字母都採用大寫字母,也被稱為 Pascal 命名法。 |
| 範例 | `firstName`、`lastName` | `FirstName`、`LastName` |
#### 語意化
注意變數名稱是否語意化,以利於後續維護與團隊協作。
* u ❓
* usr ❓
* userName ✔️
#### 名詞?動詞?
* 變數通常會使用名詞命名,並且注意單、複數。
* 布林值或函式命名時,會使用 `has`、`is` 等動詞命名。
## 原始型別介紹
### 七種原始型別
1. Boolean
2. Null
3. Undefined
4. Number
5. BigInt
6. String
7. Symbol
> 原始型別又稱基本型別,另外還有物件型別。
### typeof:查詢當前資料的型別
```javascript=
let phoneNuber = "03123123";
typeof phoneNuber; // string
```
* `phoneNuber` 儲存的資料為電話號碼,並非一組用來運算的數字,故使用字串型別儲存。
* `typeof` 會返回當前值的資料型別。
## 原始型別:Boolean
### 介紹
```javascript=
let a = 1;
let b = 2;
let c = a > b;
console.log(c); // false
typeof c; // boolean
```
* Boolean(布林),只有 `true` 與 `false` 兩個值。
* 程式判斷邏輯時,會使用到布林值。
## 原始型別:Null
### 介紹
```javascript=
let a = 1000;
console.log(a); // 1000
a = null;
console.log(a); // null
typeof a; // null
```
* Null(空值),**開發者手動賦予值**,但其值沒有存放任何資料。
* 型別為 object(根據 w3c 的說明,可以先將件事視為一個 bug)。
* 可用於清除資料使用。
## 原始型別:Undefined
### 介紹
```javascript=
let a;
console.log(a); // Undefined
typeof a; // Undefined
```
* Undefined(未定義),**程式自動判斷的型別**,表示宣告了一個變數,但未賦予值。
* 此情形程式並不會爆錯,可繼續往後執行。
* 若是出現 `not defined`,表示未宣告變數,程式會報錯。
## 原始型別:Number
### 介紹
```javascript=
let milkPrice = 10;
```
* Number(數字),Javascript 的數值型別。
* 整數、帶有小數點的浮點數字、`Infinity`(無限大)、`-Infinity`(負無限大),以及 `NaN`(Not a Number)都屬於這一類。
### 什麼是賦值運算子?
* 這個就叫賦值運算子 → `=`。
* 其功能是將「右側運算元的值」**賦予**給「左側運算元」;也就是, `x = y` 會把 `y` 的值賦予給 `x`。
| 名稱 | 簡化的運算子 | 意義 |
| -------- | ------------ | ----------- |
| 賦值 | `x = y` | `x = y` |
| 加法賦值 | `x += y` | `x = x + y` |
| 減法賦值 | `x -= y` | `x = x - y` |
| 除法賦值 | `x /= y` | `x = x / y` |
| 餘數賦值 | `x %= y` | `x = x % y` |
### 什麼是 `a++` 與 `++a`?
都是用來將變數的值增加 1 的運算符,稱之為**遞增運算符**。
| | a++ | ++a |
| ---- | -------- | ---- |
| 名稱 | 後置遞增 | 前置遞增 |
| 意義 | 先返回變數的當前值,然後再將變數加 1。 | 先將變數加 1,然後返回變數的新值。 |
### a++
```javascript=
let a = 5;
let b = a++;
console.log(a); // 6
console.log(b); // 5
```
* `let b = a++;` 時,`a` 先將當前的值(5)賦予給 `b`,接著 `a` 的值增加 1。
* 因此 `a` 的最終值為 6,`b` 的值則是 5 。
### ++a
```javascript=
let a = 5;
let b = ++a;
console.log(a); // 6
console.log(b); // 6
```
* `let b = ++a;` 時,`a` 的值增加 1(變成 6)之後,再將值賦予給 `b`。
* 因此 `a`、`b` 的最終值都是 6。
### 資料來源
* [初學者指南:JavaScript 中的 a++ 和 ++a 有什麼不同?](https://realnewbie.com/coding/javascript/javascript-post-increment-pre-increment/)
### 什麼是 NaN?
```javascript=
let a = 5;
let b = "banana";
let c = a * b;
console.log(c); // NaN
typeof c; // number
```
* NaN(Not a Number),不是數字,是 JavaScript 的特殊數值。
* 在某些無法運算的情況下,例如數字型別與文字型別**相乘**時,便會出現 **NaN**。
* NaN 的型別為數字。
## 原始型別:BigInt
### 介紹
```javascript=
const b = BigInt("1234567890123456789012345678901234567899");
console.log(b); // 1234567890123456789012345678901234567899n
typeof b; // bigint
```
* 在 JavaScript 中,`Number` 使用的是 64 位浮點數,表示能處理的數字範圍是有限的,能處理的數字範圍為 `-(2^53 - 1)` 到 `2^53 - 1`,也就是 -9007199254740991 到 9007199254740991。
* 在 ES2020 引入新的型別 `BigInt` 解決此問題。
* 可以表示非常大的整數,而不失精準度。
* 可以進行運算,但不能與 `Number` 混合運算(兩者為不同型別!)。
* `BigInt` 的除法會向下取整。
### 如何使用 BigInt?
#### 在數字後方加入 `n`
```javascript=
const b = 1234567890123456789012345678901234567899n;
console.log(b); // 1234567890123456789012345678901234567899n
typeof b; // bigint
```
#### `BigInt()`
```javascript=
const b = BigInt("1234567890123456789012345678901234567899");
console.log(b); // 1234567890123456789012345678901234567899n
typeof b; // bigint
```
### 資料來源
* [BigInt 的基礎與應用](https://ithelp.ithome.com.tw/articles/10365264)
## 原始型別:String
### 介紹
```javascript=
let apple = "蘋果";
```
* String(字串),表示字元的集合,一個字元代表一個字母。
* 需要用成對的 `"` 或 `'`,包住字串,否則程式碼會視為變數,造成預期外的狀況。
### 字串與數字相加
```javascript=
let apple = "蘋果";
let num = 10;
let res = apple + num;
typeof res; // string
```
* 字串型別可與數字型別相加,這個過程中發生了**自動轉型**,將數字型別轉型成文字型別,故 `typeof` 的結果會是字串。
### 樣版字面值
#### 使用前
```javascript=
let myName = "Josh";
let hobby = "看電影";
let content = "您好,我是" + myName + ",興趣是" + hobby;
console.log(content); // 您好,我是Josh,興趣是看電影
```
#### 使用後
```javascript=
let myName = "Josh";
let hobby = "看電影";
let content = `您好,我是 ${myName},興趣是${hobby}`;
console.log(content); // 您好,我是 Josh,興趣是看電影
```
* 使成對的反引號包住字串,並用 `${}` 引入變數資料。
## 原始型別:Symbol
### 介紹
```javascript=
let appleOne = Symbol("apple");
let appleTwo = Symbol("apple");
console.log(appleOne === appleTwo); // false
// typeof appleOne; // Symbol
// typeof appleTwo; // Symbol
```
* ES6 引入的新型別,用來創建獨一無二的識別值。
* 即使兩個 Symbol 內容相同,但也會被電腦視為不同的值。
* 可作為物件的屬性名稱,常用於避免屬性名稱衝突的情況。
### 如何使用 Symbol?
#### `Symbol()`
```javascript=
const sym1 = Symbol();
const sym2 = Symbol('desc');
const sym3 = Symbol('desc');
console.log(sym2 === sym3); // false
console.log(sym2.description); // desc
```
* 每個 Symbol 創建的值都是唯一的,故 sym2 與 sym3 不相等。
* 使用 `description` 取得描述內容。
### 實際應用
#### 未使用 Symbol
```javascript=
let nameList = {
"John" : { yearsOld : 10 },
"John" : { yearsOld : 9 }
}
console.log(nameList); // John : { yearsOld : 9 }
```
* 物件中出現相同的屬性名稱時,後方的屬性名稱會蓋掉前方的,因此最後只剩下 `John : { yearsOld : 9 }` 這個值。
#### 使用 Symbol
```javascript=
let nameList = {
[Symbol("John")] : { yearsOld : 10 },
[Symbol("John")] : { yearsOld : 9 }
}
console.log(nameList);
// Symbol(John) : {yearsOld: 10}
// Symbol(John) : {yearsOld: 9}
```
* 在物件中,使用 `Symbol()` 建立屬性名稱,能確保每一個屬性名稱都是唯一的,因此後方的 John 不會蓋掉前方的 John。
* 使用 `[]` 包住 `Symbol()` 建立的屬性名稱,否則會被視為字串。
### 資料來源
* [資料型別 Symbol 使用時機](https://ithelp.ithome.com.tw/articles/10220499)
## 自動轉型與強制轉型
{%preview https://hackmd.io/@SorryFish/SkZEQv9Cle %}
## 淺談陣列與物件
### 什麼是陣列?什麼是物件?
陣列與物件是一種資料集合,能幫助開發者更有效率的管理與設計資料。
### 範例
> 情境:紀錄水果行的資料。
#### 未使用陣列與物件
```javascript=
const shopName = "fruitShop";
let appleNum = 10;
let bananaNum = 20;
let boxNum = 100;
```
#### 使用陣列與物件
```javascript=
const shopDate = {
"shopName" : "fruitShop",
"fruits" : ["apple","banana"],
"boxNum" : 100
}
```
在資料不多的情況下,也許能單獨將資料儲存於變數上,但絕大多數的形況:
1. 資料數龐大。
2. 需依照開發需求,設計資料結構。
3. 需考量後續的維護與可讀性。
因此需要使用陣列與物件的概念儲存資料。
## 陣列
### 宣告陣列
```javascript=
let arr = [];
let data = ["abc", 10, false];
let fruits = ["apple", "banana"];
let fruitsNum = [10, 5];
let number = [1, 2, 3, 4, [23, 24]];
```
* 一個清單,由元素和索引構成。
* 使用 `[]` 包住資料,並用 `,` 隔開每一筆資料(最後一筆不用 `,`)。
* 陣列內可以是空值、任何型別的資料,甚至是陣列或物件。
### 讀取與儲存陣列資料
```javascript=
let fruits = ["apple", "banana"];
console.log(fruits[0]); // "apple"
let hotItem = fruits[0];
console.log(hotItem); // "apple"
```
* `變數 [ 索引值 ] ;`
* 有些程式語言,基於記憶體位置的「偏移量」,會從 0 開始計算,因此在上述的範例中,若要取出 `"apple"`,索引值要輸入 0 。
* `let hotItem = fruits[0];` 將 `fruits[0]` 的結果,賦予在另一個變數上,就能單獨使用其資料。
### 資料來源
* [為什麼索引值從 0 開始算?](https://kaochenlong.com/2024/03/03/why-array-index-begin-with-zero.html)
* [物件與陣列設計](https://hackmd.io/bA80b0sqSPe2wX6vGGC2VA?view)
### 讀取與儲存陣列長度
```javascript=
let fruits = ["apple", "banana"];
console.log(fruits.length); // 2
let fruitsNum = fruits.length;
console.log(fruitsNum); // 2
```
* `變數.length`:返回陣列中有幾筆資料。
### 寫入資料
#### 按照索引位置寫入
```javascript=
let fruits = [];
fruits[0] = "apple";
fruits[1] = "banana";
fruits[2] = "melon";
console.log(fruits); // ['apple', 'banana', 'melon']
```
* `變數 [ 索引值 ] = 值;`
#### 不照索引位置寫入
```javascript=
let fruits = [];
fruits[0] = "apple";
fruits[1] = "banana";
fruits[2] = "melon";
fruits[5] = "lemon";
console.log(fruits); // ['apple', 'banana', 'melon', 空白 × 2, 'lemon']
```
* 未賦予值的索引位置為空值。
* `length` 的結果會是 6,空值也是值。
#### `push`
```javascript=
let fruits = ["apple", "banana"];
fruits.push("lemon");
console.log(fruits); // ['apple', 'banana', 'lemon']
```
* `變數.push ( 要寫入的值 );`
* `push` 為 JavaScript 提供的方法,可將資料寫入至陣列**末端**。
## 物件
### 宣告物件
```javascript=
let band = {
genres : ["progressive rock", "indie rock", "post-punk"],
instruments : ["vocals", "guitar", "bass", "drum"],
membersNum : 3,
isActive : true
};
```
* 一個物體,由鍵與值構成與描述。
* `let 變數 = { 屬性 : 值 };`
* 屬性=key;值=value。
* 物件內可以是空值、任何型別的資料,甚至是陣列或物件。
### 讀取與儲存物件資料
```javascript=
const band = {
genres : ["progressive rock", "indie rock", "post-punk"],
instruments : ["vocals", "guitar", "bass", "drum"],
membersNum : 3,
isActive : true
};
console.log(band.genres); // ['progressive rock', 'indie rock', 'post-punk']
const bandGenres = band.genres;
console.log(bandGenres); // ['progressive rock', 'indie rock', 'post-punk']
```
* `變數.屬性`:返回儲存的值。
* `let bandGenres = band.genres;` 將 `band.genres` 的結果,賦予在另一個變數上,就能單獨使用其資料。
### 寫入資料
```javascript=
const band = {};
band.genres = ["progressive rock", "indie rock", "post-punk"];
band.instruments = ["vocals", "guitar", "bass", "drum"];
band.membersNum = 3;
band.isActive = true;
console.log(band);
//genres : ["progressive rock", "indie rock", "post-punk"]
//instruments : ["vocals", "guitar", "bass", "drum"]
//membersNum : 3
//isActive : true
```
* `變數.屬性 = { 要寫入的值 };`
## 原始型別傳值 vs 物件型別傳址
### 原始型別傳值
* 將值複製一份,放到新的記憶體位置給新的變數使用。
* 新、舊變數的記憶體位置不同,因此後續有任何變更,都不會互相影響。
```JavaScript
let a = 1;
let b = a;
b = 2;
console.log(a, b); // 1, 2
```
| 變數 | 記憶體位置 | 值 |
| ---- | ---------- | ----------------------------------- |
| a | 0x01 | 1 |
| b | 0x02 | ~~1(a 的值)~~ → 2(重新賦值到 b) |
### 物件型別傳址
* 傳值又稱傳參考。
* 將變數參考的記憶體位置,傳到新的變數上。
* 此時新、舊變數參考的記憶體位置是同一個,因此後續有任何變更,都會互相影響。
```javascript=
let arrA = [1, 2, 3];
let arrB = a;
arrB.push(50);
console.log(arrA, arrB); // [1, 2, 3, 50] [1, 2, 3, 50]
```
| 變數 | 記憶體位置 | 值 |
| ---- | ---------- | ---------------------------------------------------- |
| arrA | 0x03 | ~~[1, 2, 3]~~ **[1, 2, 3, 50]**(跟著變更) |
| arrB | 0x04 | ~~0x03([1, 2, 3])~~ **[1, 2, 3, 50]**(`push` 50) |
### 為何可以修改 const 宣告的物件?
```javascript=
const user = { name: "John", age: 20 };
// 允許修改屬性。
user.age = 21;
user.name = "Wick";
// 不允許變數重新賦值,這代表指向的記憶體位置會被修改。
user = { name: "New User" };
```
* 嚴格來說,可以修改的是 const 宣告的物件「內容」。
* const 限制的是變數指向的**記憶體位置**,而該位置中的物件內容(屬性)是可以修改的。