避開魔法字串 - constant 和 enum object 使用時機
何謂魔法字串
在程式設計中直接使用一般字串進行硬編碼 (hard coded),是非常不推薦的,我們可以參考以下範例
// bad: 魔法字串
if (user.role === "admin") {
// ...
}
或許你會覺得這例子還好,也很好懂,但是一旦程式碼規模開始龐大,也把許多 function export 給許多地方使用的時候,我們會很難追查這些字串是什麼意思
(雖然 switch case 不是很被推薦,但目前程式碼很多這樣的情境)
// src/very/very/very/very/deep/fn.js
function beNinja(type, value) {
switch (type) {
case "heheMagic":
break;
case "surprise":
break;
case "rickRoll":
break;
case "ninja1":
break;
case "ninja2":
// ...
default:
}
}
// src/so/so/so/deep/MyGod.jsx
beNinja("ninja", value);
// src/so/so/so/so/so/deep/HolyCow.jsx
beNinja("heheMagic", value);
當你看到這些寫死的字串以參數的方式傳入函數,==後人要理解邏輯或追查起來會非常花時間==,並且還有其他缺點
- 因為是寫死的,如果要更改,需要直接以 IDE 的搜尋取代,其中這非常容易出包
- 以寫死的字串驅動程式碼運作,你會發現 debug 到天荒地老,才發現是打錯字,因為字串打錯在 IDE 上幾乎沒任何明顯提示
好的作法
// good: 使用 constant 替代魔法字串
const ROLE_ADMIN = "admin";
if (user.role === ROLE_ADMIN) {
// ...
}
其實更建議的作法是將 constant 集結起來變成一個 js 檔,放在 constants 資料夾 (當然其他也要是情況而定)
假設今天需要把 admin
變更成 administer
,只要在源頭 constant.js 進行改動,其他 import 進來的資料夾都會雨露均沾了
另外一個好處是,因為已經化成變數,變數在 IDE 的提示和支援就強大很多,==打錯會很明顯知道==
延伸作法 : 類 Enum 物件
因為純 JavaScript 沒有 真正的 enum,我們可以使用物件來達到類似的效果
const DIRECTIONS = {
WEST: "west",
EAST: "east",
SOUTH: "south",
NORTH: "north",
};
// 通常使用大寫和底線
直接調用時就直接使用 DIRECTIONS.NORTH
來指向真實的字串內容,除了能獲得良好的 IDE 提示之外,也明確表達字串的使用範圍都在這個 object 裡面
暫時改不動又難維護的程式,先自建 Enum Object
曾經有一個共用 modal 元件,它的設計是傳入一個 close handler 作為 prop
內部的設計是,只要按下關閉,元件內部就會執行 handleClose(0)
,按下確認,元件內部就會執行 handleClose(1)
(厲害了)
如果需要調用這個 modal,外層設計 handler 的時候就要 if 1 then... if 0 then...
可讀性會非常的差,例如
function handleClose(closeType) {
if (closeType === 0) {
//...
} else {
//...
}
}
return <CoolModal onClose={handleClose} />;
但如果這個 Modal 目前還改不動,那只好自己做個 enum object 來讓可讀性好一點了
const MODAL_CLOSE_TYPE = {
CLOSE: 0,
CONFIRM: 1,
};
function handleClose(closeType) {
if (closeType === MODAL_CLOSE_TYPE.CONFIRM) {
// ...
}
if (closeType === MODAL_CLOSE_TYPE.CLOSE) {
// ...
}
}
return <CoolModal onClose={handleClose} />;