---
# System prepended metadata

title: 筆記｜第一堂：認識 JavaScript
tags: [學習筆記, JavaScript]

---

`#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 限制的是變數指向的**記憶體位置**，而該位置中的物件內容（屬性）是可以修改的。