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