JavaScript 的 singleton 可以用 class 或 ES module 兩種方式實作,兩者設計理念有明顯差別。123
核心比較
| 特色 | Class Singleton | ES Module Singleton |
|---|---|---|
| 基本原理 | 透過 class static 欄位紀錄唯一 instance,只能透過 getter 取得 | 利用 ES module cache,每次 import 都是同一份物件 |
| 封裝能力 | 可用 # 私有欄位或 closure 隱藏細節,可自定 instance 行為 | module 變數為私有,只有 export 部分可被外部存取 |
| 多份 instance | 可視需求設計允許參數化 support 多 singleton(如多 db),也可全域 unique | 全檔案全域唯一,通常無法有多個,不利參數化 |
| TypeScript 支援 | 完全 type-safe,適合複雜 OOP case | 純物件、function 較平面,少 OOP 結構 |
| 用法 | const s = Singleton.getInstance() | import singleton from './mySingleton.js' |
| 可測試性 | 容易 mock class or static method,彈性高 | jest 需用 ES module mock,寫法較直覺但彈性較低 |
| 熱重載(hot reload) | 理論支援,instance 維持於 module memory | 若是 node/hmr,module reload 會讓 singleton 重產 |
| 輕鬆支援單元測試 | 可以 new 多次 class,stub static property | module pattern 不易在 runtime 換掉 |
實務建議
- 單純 config 狀態、純 data 推薦直接用 module 實作 singleton,語法單純。1
- 需要多參數、OOP 或複雜邏輯/初始化流程,或日後需支援多份變體,用 class 型 singleton 預留彈性較佳。3
- 若只做單純全域物件共用(如 logger, config, cache),兩種都可行,選團隊習慣即可。
例子簡述
Class Singleton:
class Singleton {
static #instance = null;
static getInstance() {
if (!Singleton.#instance) Singleton.#instance = new Singleton();
return Singleton.#instance;
}
}ES Module Singleton:
// mySingleton.js
const instance = { ... };
export default instance;兩者共同點是「同一個專案生命週期,只會有一份物件」;差異在於一個用 OOP class 控制,一個靠 module loading/scope 實現唯一性。231