# JS 程式碼改造計畫:從揪出異味到實現 Cl# JS 程式碼改造計畫:從揪出異味到實現 Clean Code - Sunny Wu
{%hackmd @JSDC-tw/B1loEcwJZl %}
###### tags: `JSDC2025`
Slido:https://app.sli.do/event/r8dpNR9ocUMEmmcoN1UaaN
> 開始做筆記
- 不管是什麼語言 clean code 的原則都是相通的
- 今天分享以前在專案中踩的坑跟解決方式
## 程式碼異味一:波動拳
### 心法一:儘早返回
- 定義好你的資料流、扁平化
- 可以讓程式碼的 flow 呈現一直線(線性流程)
```js
function checkValue(a, b, c, d, e) {
if (a > 0) {
if (b > 0) {
if (c > 0) {
if (d > 0) {
if (e > 0) {
return "All positive";
} else {
return "e is not positive";
}
} else {
return "d is not positive";
}
} else {
return "c is not positive";
}
} else {
return "b is not positive";
}
} else {
return "a is not positive";
}
}
```
```js
function checkValue(a, b, c, d, e) {
if (a <= 0) return "a is not positive";
if (b <= 0) return "b is not positive";
if (c <= 0) return "c is not positive";
if (d <= 0) return "d is not positive";
if (e <= 0) return "e is not positive";
return "All positive";
}
```
## 程式碼異味二:千層麵
- 上帝物件(god object)
- 一薪多用
### 心法二:單一職責原則 SRP
- 一個函式只做一件事
- 拆分純函式與組合函式
範例:一個函式同時做三個驗證+登入
拆成 三個純函式+一個組合函式
```js
// 純函式
function vaildateInput (username, password) {
// 驗證格式
}
function verifyPassWord(username, password){
// 檢查密碼
}
function logLogin(username){
// 記錄登入
}
// 組合函式
function handleUserLogin (username, password) {
if (!vaildateInput(username, password) return false;
if (!verifyPassword(username, password) return false;
logLogin(username);
return true;
}
```
## 程式碼異味三:義大利麵
例如:
- UI 邏輯、資料處理、 API 呼叫、驗證規則、全部寫在一起
- 一個 .vue 寫了一兩千行
### 心法三:分層架構+關注點分離 SoC
- UI 層:只負責顯示
- 組合層:負責組合功能
- 資料層+邏輯層
- 資料層:只取 API
- 邏輯層:只負責驗證、格式化
```
UI 層 -> 組合層 -> { 邏輯層 + 資料層 }
TodoList.vue -> useTodos() -> validateTodo()
-> formatTodo()
-> fetchTodos()
```
## 改造前的起手式
測試就是你的安全網
有各種測試類型:
單元測試
整合測試
端對端測試
元件測試
UX 測試
視覺回歸測試
資料庫測試
...etc
### 特徵測試
1. 給定一組輸入 -> 紀錄實際輸出
2. 把這個輸入輸出關係寫成測試
3. 重構時,只要輸出還是一樣的,就代表行為沒變
輸出變了就知道改壞了
邊改程式就可以邊測(去看結果有沒有變)
範例:
```js
function calculatePrice(price, vip) {
return vip ? price * 0.8 : price;
}
// 一樣的 input 和 output
test('VIP客戶有 8 折', () => {
expect(calculatePrice(100, true)).toBe(80);
})
test('一般客戶原價', () => {
expect(calculatePrice(100, false)).toBe(100);
})
```
## 我們的下一步
修改頻率低或比較不重要的功能,就沒有必要重構
把精力放在高價值、有風險的程式區塊
### 三個問題
- 新人看得懂嗎?
- 能單獨測試嗎?
- 修改安全嗎?
好的程式碼在五分鐘內就可以抓到重點
看函式的命名如果不能看出來用途,可能就要拆分
一個函式要呼叫另一個函式才能運作 -> 耦合太緊
### 可讀性 > 炫技
避免用縮寫:
X usr
O user
## 結語
降低我們的壓力
節省我們的時間
讓程式碼告訴我們意圖,而不是去猜
> 聊天區
---
{%hackmd @JSDC-tw/jsdc2025_sponsor %}