# 🏅 Day 6 - 列舉型別(Enum)
列舉(Enum)型別用於取值,在 C# / C / JAVA 常見到它的身影,而 JavaScript 本身是沒有此語言特性的。
**列舉適合被限制在一定範圍內的場景**,例如一週只能有七天,紅綠燈有紅、綠、黃的狀態。
```tsx=
enum Weekday {
Sunday,
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday
}
// 使用列舉
console.log(Weekday.Monday);
// 印出 1
```
為何顯示為 1 呢?這是因為列舉成員被初始化時,會從 0 開始按順序賦予遞增的數值,而且還能夠實現從列舉的值到其名稱的反向映射。
```tsx=
enum Weekday {
Sunday, // 賦值為 0
Monday, // 賦值為 1
Tuesday, // 賦值為 2
Wednesday, // 賦值為 3
Thursday, // 賦值為 4
Friday, // 賦值為 5
Saturday // 賦值為 6
}
// 範例一:使用數字來取得對應的列舉名
let dayName = Weekday[2]; // dayName 值為 "Tuesday"
// 範例二,顯示今天禮拜幾
let today = Weekday.Monday; // today 值為 1
```
這段輸出 JavaScript 的話,會變成如下,好讓 JavaScript 也擁有列舉功能
```tsx=
"use strict";
var Weekday;
(function (Weekday) {
Weekday[Weekday["Sunday"] = 0] = "Sunday";
Weekday[Weekday["Monday"] = 1] = "Monday";
Weekday[Weekday["Tuesday"] = 2] = "Tuesday";
Weekday[Weekday["Wednesday"] = 3] = "Wednesday";
Weekday[Weekday["Thursday"] = 4] = "Thursday";
Weekday[Weekday["Friday"] = 5] = "Friday";
Weekday[Weekday["Saturday"] = 6] = "Saturday"; // 賦值為 6
})(Weekday || (Weekday = {}));
// 範例一:使用數字來取得對應的列舉名
let dayName = Weekday[2]; // dayName 值為 "Tuesday"
// 範例二,顯示今天禮拜幾
let today = Weekday.Monday; // today 值為 1
```
## enum 手動賦值
除此之外 enum 也可以賦值為其它型別的值,方便開發者運用:
```tsx=
enum Weekday {
Sunday = "Sun",
Monday = "Mon",
Tuesday = "Tue",
Wednesday = "Wed",
Thursday = "Thu",
Friday = "Fri",
Saturday = "Sat"
}
// 範例一:使用列舉名來取得對應的字串簡寫
let dayName = Weekday.Tuesday; // dayName 值為 "Tue"
// 範例二:顯示今天星期幾(以簡寫形式)
let today = Weekday.Monday; // today 值為 "Mon"
```
如果有反向映射的需求,可以用以下的方式來取出 key 值:
```tsx=
enum Weekday {
Sunday, // 賦值為 0
Monday, // 賦值為 1
Tuesday, // 賦值為 2
Wednesday, // 賦值為 3
Thursday, // 賦值為 4
Friday, // 賦值為 5
Saturday // 賦值為 6
}
// 範例三:使用列舉的值取出 key 值
let todayKey = Weekday[1]; // todayKey 值為 "Monday"
```
## 範例:紅綠燈
像是紅綠燈它就是屬於狀態限制在一定範圍,也就是只有紅綠黃色,就相當適合使用 enum 來定義。
### 沒使用 enum 的話
在我們寫程式時,通常會用數字來代表不同的狀態。在技術上雖然可行,缺點就是可讀性較差,如下程式碼範例:
```tsx=
// 定義紅綠燈狀態的數字
const RED = 0;
const YELLOW = 1;
const GREEN = 2;
// 當前紅綠燈狀態
let currentLight = RED;
// 檢查紅綠燈狀態
function checkTrafficLight(status: number) {
if (status === RED) {
console.log("紅燈,請停止!");
} else if (status === YELLOW) {
console.log("黃燈,請注意!");
} else if (status === GREEN) {
console.log("綠燈,可以行駛!");
} else {
console.log("未知燈號!");
}
}
// 檢查當前燈號
checkTrafficLight(currentLight);
```
### 有用 enum 範例
編號完全消失不見,另外藉由參數上的型別註釋,可讀性與維護性也大為提升:
```tsx=
enum TrafficLight {
Red, // 紅燈
Yellow, // 黃燈
Green // 綠燈
}
// 當前紅綠燈狀態
let currentLight = TrafficLight.Red;
// 檢查紅綠燈狀態
function checkTrafficLight(status: TrafficLight) {
switch (status) {
case TrafficLight.Red:
console.log("紅燈,請停止!");
break;
case TrafficLight.Yellow:
console.log("黃燈,請注意!");
break;
case TrafficLight.Green:
console.log("綠燈,可以行駛!");
break;
default:
console.log("未知燈號!");
}
}
// 檢查當前燈號
checkTrafficLight(currentLight);
```
## 待辦事項範例程式碼
```tsx=
enum TodoStatus {
Pending, // 待辦事項尚未開始
InProgress, // 待辦事項正在進行中
Completed, // 待辦事項已完成
Cancelled // 待辦事項被取消
}
// 待辦事項陣列
let todos = [];
// 新增待辦事項的函數
function addTodoItem(name: string) {
const newTodo = {
name: name,
status: TodoStatus.Pending // 新增的待辦事項默認狀態為 "Pending"
};
todos.push(newTodo);
console.log(`新增待辦事項: "${name}"`);
}
// 範例:新增一個待辦事項
addTodoItem("完成 TypeScript 專案");
// 顯示目前所有待辦事項
console.log(todos);
```
## 列舉、元組、陣列
蠻多開發者比較難判斷何時用這三種資料結構,以下摘要分享:
| 特性/用途 | 列舉 (Enum) | 元組 (Tuple) | 陣列 (Array) |
| --- | --- | --- | --- |
| 用途定義 | 表示一組相關的資料集合,如狀態或選項。 | 儲存固定數量、固定順序的多種型別的值。 | 儲存多個相同型別的值。 |
| 索引方式 | 通過名稱或數值索引。 | 通過位置索引。 | 通過位置索引。 |
| 語義清晰性 | 高(每個值都有明確的名稱)。 | 中(位置重要,但型別可以變)。 | 低(因會儲存多資料內容)。 |
| 例子 | `enum Color { Red, Green, Blue }` | `let point: [number, number] = [7, 5];` | `let numbers: number[] = [1, 2, 3];` |
| 應用場景 | 固定的選項(如天數、狀態)。 | 當需要固定組合的不同型別值時(如座標、excel 欄)。 | 儲存一系列相同型別的元素(例如數據列表)。 |
### 使用情境:
1. 如果你需要表示一組固定的選項,如天數、方向、狀態,可以使用 **列舉** **`enum`**。
2. 假使你需要存儲固定長度的多種類型的數據(如座標、excel 欄),可以使用**元組**。
3. 需要儲存大量相同型別數據的情況,如數據列表,可以使用**陣列**。
## 題目
請觀察以下情境,並推斷他適合哪種型別格式:
1. 情境:你需要儲存不同的使用者角色,如「管理員」、「使用者」和「訪客」。這些角色是固定的且有特定的名稱。
A. 列舉 (Enum)
B. 元組 (Tuple)
C. 陣列 (Array)
2. 情境:你正在開發一個函式,需要回傳一個包含經度和緯度的地理座標。
A. 列舉 (Enum)
B. 元組 (Tuple)
C. 陣列 (Array)
3. 情境:你需要一個儲存全台灣各家庭的數據資料,例如每個家庭的總年收入。
A. 列舉 (Enum)
B. 元組 (Tuple)
C. 陣列 (Array)
4. 情境:你需要表示一週的七天,每天都有一個特定的名稱。
A. 列舉 (Enum)
B. 元組 (Tuple)
C. 陣列 (Array)
5. 情境:你需要追蹤一個電子商務網站上的產品類別,這些類別已經固定,不會隨時間而更改。
A. 列舉 (Enum)
B. 元組 (Tuple)
C. 陣列 (Array)
## 實作題
### **1. 設計一個訂單處理系統**
### 情境描述:
你正在開發一個電子商務系統,需要處理不同階段的訂單。為了更好地追蹤和管理訂單,你決定使用 TypeScript 的 **`enum`** 來表示訂單的各個處理階段。
### 任務:
1. 定義一個名為 **`OrderStatus`** 的 **`enum`**,其中包括以下狀態:待處理(Pending)、運送中(Shipping)、已送達(Delivered)、已取消(Cancelled)。
2. 建立一個函式,名稱為`changeOrderStatus`,藉此更新訂單狀態
- 參數:訂單 ID (**`orderId`**) 和新的訂單狀態 (**`newStatus`**)。
- 回傳:無(僅在控制台印出更新訊息)。
### 部分程式碼:
```tsx
enum OrderStatus
// 更新訂單狀態
function changeOrderStatus(orderId, newStatus) {
console.log(`訂單 ${orderId} 的狀態已更新為:${newStatus}`);
}
// 範例使用
let orderId = 123; // 隨機賦予的訂單 ID
changeOrderStatus(orderId, 訂單狀態);
```
### **2. 設計一個機器狀態管理系統**
### 情境描述
你正在設計一個機器的工作狀態管理追蹤系統,以方便控管每臺機器的運作情形。請運用 TypeScript 的 **`enum`** 來表示機器的各階段狀態,並為機器定義一個 **`type Machine`** 來作為函式的傳入值。
### 任務
1. 定義一名稱為 **`MachineStatus`** 的 **`enum`**,包含 **`Idle`**、**`Working`** 和 **`Error`** 三種狀態。
2. 定義一個 **`Machine`** 型別來記錄機器的 **`id`** (**`string`**) 和 **`status`** (**`MachineStatus`**)。
3. 設計一個 **`updateMachineStatus`** 函式來更新機器的狀態。
### 部分程式碼
```tsx
// TODO: Enum: 機器狀態
enum MachineStatus
// TODO: Type: 機器型別
type Machine
// 更新狀態函式
function updateMachineStatus(machine, newStatus) {
machine.status = newStatus;
console.log(`機器 ${machine.id} 狀態更新為 ${newStatus}`);
}
// 範例使用
let machine: Machine = { id: "D001", status: <機器狀態> };
updateMachineStatus(machine, <機器新狀態>);
```
<!-- 解答:
單選題目:ABCAA
實作題:
1.
enum OrderStatus {
Pending, // 待處理
Shipping, // 運送中
Delivered, // 已送達
Cancelled // 已取消
}
function updateOrderStatus(orderId: number, newStatus: OrderStatus) {
console.log(`訂單 ${orderId} 的狀態已更新為:${OrderStatus[newStatus]}`);
}
// 示範
let orderId = 123; // 假設的訂單 ID
updateOrderStatus(orderId, OrderStatus.Pending);
updateOrderStatus(orderId, OrderStatus.Shipping);
updateOrderStatus(orderId, OrderStatus.Delivered);
2.
// Enum: 機器狀態
enum MachineStatus {
Idle = "Idle",
Working = "Working",
Error = "Error"
}
// Type: 機器型別
type Machine = {
id: string;
status: MachineStatus;
};
// 更新狀態函式
function updateMachineStatus(machine: Machine, newStatus: MachineStatus): void {
machine.status = newStatus;
console.log(`機器 ${machine.id} 狀態更新為 ${newStatus}`);
}
const machine: Machine = { id: "D001", status: MachineStatus.Idle };
updateMachineStatus(machine, MachineStatus.Working);
```