JavaScript Design Pattern 筆記 — Module/Revealing Module
Module Pattern 和 Revealing Module Pattern 都是 JavaScript 中很常見的設計模式,用於組織代碼並創建私有和公開的成員。這兩種模式主要用於模擬封裝,以便更好地組織代碼並控制成員的可訪問性。
JavaScript Design Pattern 筆記 — Module/Revealing Module

Module Pattern 和 Revealing Module Pattern 都是 JavaScript 中很常見的設計模式,用於組織代碼並創建私有和公開的成員。這兩種模式主要用於模擬封裝,以便更好地組織代碼並控制成員的可訪問性。
1. Module Pattern
情境
Module Pattern 是在 JavaScript 早期用來創建封裝結構的模式,適合在需要模擬私有變數和方法的情境下使用。透過此模式,可以將一組函數和變數封裝在模組內,並只暴露部分成員供外部使用。
解決問題
- 提供私有作用域:可以隱藏不應公開的成員。
- 避免全域作用域污染:使代碼更具結構性,避免變數和函數污染全域命名空間。
- 模擬封裝:增強代碼的模組化和可維護性。
取捨
- 優點:提高代碼可讀性和安全性,確保僅公開必要的成員。
- 缺點:在大型應用中可能會變得複雜,且有時過多的封裝反而降低靈活性。
範例
const Module = (function() {
// 私有變數和方法
let privateVar = "I am private";
function privateMethod() {
console.log(privateVar);
}
return {
// 公開成員
publicMethod: function() {
privateMethod();
console.log("I am public");
}
};
})();
Module.publicMethod(); // 可以訪問
Module.privateMethod(); // 無法訪問,會報錯
在 JavaScript 中,可以透過引入 Mixin 和 WeakMap 來擴展 Module Pattern 的功能,使代碼更加靈活、增強封裝性並提升私有資料的安全性。
使用 Mixin 擴展 Module Pattern
Mixin 是一種重用程式碼的方法,允許你將一組功能添加到多個物件中,而無需使用繼承。Mixin 通常用來將通用的功能組合到一個模組中,然後將該模組的功能引入到其他模組中。
情境
當多個模組需要一些共通的功能或方法,但並不需要完全繼承時,可以用 Mixin 將這些功能引入至 Module Pattern 中。
範例
假設我們有一個日誌功能的 Mixin,需要在不同的模組中使用:
const loggerMixin = {
log: function(message) {
console.log(`[LOG]: ${message}`);
},
error: function(message) {
console.error(`[ERROR]: ${message}`);
}
};
// 主模組
const ModuleWithMixin = (function(mixin) {
let privateVar = "private data";
function privateMethod() {
mixin.log("Logging from private method");
}
function publicMethod() {
mixin.log("Logging from public method");
privateMethod();
}
// 將 mixin 中的成員加入模組公開方法中
return {
...mixin, // 混入 Mixin
publicMethod
};
})(loggerMixin);
ModuleWithMixin.publicMethod(); // 可以訪問 mixin.log 和 mixin.error
在上例中,loggerMixin 是一個可以被不同模組重用的 Mixin,模組可以在自己的邏輯中使用 Mixin 提供的日誌功能。
使用 WeakMap 擴展 Module Pattern
WeakMap 是 JavaScript 中一種特殊的資料結構,允許使用物件作為 key,並且這些 key 在無其他參考時會自動被垃圾回收。WeakMap 常用於將私有資料綁定至物件,避免這些資料在模組外部被直接存取。
情境
當需要在物件中存放完全私有的資料,且不希望被外部訪問時,可以使用 WeakMap 來模擬私有資料的存取。這在模組或類別中封裝私有變數和方法時非常實用。
範例
以下是用 WeakMap 模擬 Module Pattern 中私有變數的用法:
const ModuleWithWeakMap = (function() {
// 使用 WeakMap 存放私有資料
const privateData = new WeakMap();
function Module() {
// 為每個實例設定私有變數
privateData.set(this, { secret: "I am private data" });
}
Module.prototype.getSecret = function() {
// 訪問私有變數
return privateData.get(this).secret;
};
Module.prototype.setSecret = function(newSecret) {
// 修改私有變數
privateData.get(this).secret = newSecret;
};
return Module;
})();
const instance = new ModuleWithWeakMap();
console.log(instance.getSecret()); // "I am private data"
instance.setSecret("New private data");
console.log(instance.getSecret()); // "New private data"
在此範例中,privateData 是一個 WeakMap,它將物件的私有數據與模組實例綁定,使得該私有數據無法被外部直接訪問。
- Mixin 可以用來將共用功能注入至模組中,實現功能重用並簡化代碼結構。
- WeakMap 是用於存放模組內的私有資料的強大工具,使得私有變數只能在模組內部訪問,增強封裝性和安全性。
2. Revealing Module Pattern
情境
Revealing Module Pattern 是 Module Pattern 的改良版,用於更加直觀地管理私有和公開的成員。在此模式中,先定義所有函數和變數,然後在返回的物件中指定哪些成員為公開成員。這個模式主要用於希望代碼結構更清晰時。
解決問題
- 提供更簡潔的結構:公開成員直接定義於返回物件中,提升可讀性。
- 更易於管理公開和私有成員:能直觀地看到哪些方法和變數對外部可見。
取捨
- 優點:明確公開成員的列表,提高代碼可讀性。
- 缺點:需要小心命名衝突,可能會隱藏成員之間的依賴關係。
範例
const RevealingModule = (function() {
let privateVar = "I am private";
let publicVar = "I am public";
function privateMethod() {
console.log(privateVar);
}
function publicMethod() {
console.log(publicVar);
privateMethod();
}
// 明確定義哪些為公開成員
return {
publicMethod: publicMethod,
publicVar: publicVar
};
})();
RevealingModule.publicMethod(); // 可以訪問
RevealingModule.privateMethod(); // 無法訪問
總結
- Module Pattern:適合需要更嚴格控制公開和私有成員的情境,能提供較高的封裝性,但可能增加代碼複雜性。
- Revealing Module Pattern:公開成員列表更清晰,容易管理和維護,但需要小心名稱衝突。
順便講一下垃圾回收機制(Garbage Collection,GC),一種自動管理記憶體的技術,主要用於釋放程式中不再使用的記憶體空間,以防止記憶體洩漏,從而優化應用程式的效能。在 JavaScript 等高階語言中,垃圾回收是自動執行的,開發人員不需要手動釋放記憶體。
垃圾回收的基本概念
程式運行過程中會分配記憶體來儲存變數、物件和函數。當這些變數和物件不再需要時(即程式中無法再訪問它們),它們就成為「垃圾」。垃圾回收機制會自動檢查並清除這些垃圾資料,以便釋放被佔用的記憶體空間。
常見的垃圾回收演算法
JavaScript 引擎中的垃圾回收通常是透過以下兩種方法來實現的:
1. 引用計數(Reference Counting)
引用計數會追蹤每個物件的引用數量,每次有一個新的引用時,計數加一;當引用被移除時,計數減一。如果計數變為零,則表明該物件不再需要,垃圾回收器就會回收這個物件。
優點:能快速判定物件是否不再使用。 缺點:無法處理循環引用,例如物件 A 引用物件 B,而物件 B 同時也引用物件 A,兩者都無法被回收。
範例
let obj1 = {};
let obj2 = obj1; // obj1 和 obj2 引用同一個物件
obj1 = null; // obj1 不再引用物件,但 obj2 還在引用它
// 因此該物件不會被回收
2. 標記-清除(Mark-and-Sweep)
標記-清除算法會將所有在作用域中的變數標記為「活著」的,然後掃描所有物件,並釋放沒有被標記的物件。這樣可以解決引用計數法無法處理的循環引用問題。
- 標記:從根物件(例如全域物件)開始,標記所有可訪問的物件。
- 清除:標記完畢後,回收所有未標記的物件。
優點:能有效解決循環引用問題。 缺點:需要暫時中斷程式執行來進行回收,可能導致一些性能上的延遲。
現代 JavaScript 引擎中的垃圾回收
JavaScript 引擎(如 V8)會使用標記-清除的變體,稱為分代垃圾回收,將記憶體分為兩代:
- 新生代:短期物件,創建並很快變得無用(例如局部變數)。
- 老生代:存活更久的物件。
在此機制中,新生代的垃圾回收比較頻繁,而老生代的回收則較少發生,這樣可以提高整體回收效率。
垃圾回收的優勢和限制
- 優勢:簡化記憶體管理,自動釋放無用資源,防止記憶體洩漏。
- 限制:回收過程中可能導致應用程式暫時停止;且對大型應用程式來說,垃圾回收的處理時間會隨著記憶體佔用量增大而增加。
垃圾回收機制讓開發人員無需手動釋放記憶體,極大地提升了高階語言的開發效率,但同時也需要理解如何有效管理變數範疇和引用,避免因不當引用而導致的記憶體洩漏問題。
메타데이터
- post_id
- 96ae0912ca69
- slug
- javascript-design-pattern-筆記-module-revealing-module-96ae0912ca69
- url
- https://medium.com/@yihengwu/javascript-design-pattern-%E7%AD%86%E8%A8%98-module-revealing-module-96ae0912ca69
- canonical_url
- https://medium.com/@yihengwu/javascript-design-pattern-%E7%AD%86%E8%A8%98-module-revealing-module-96ae0912ca69
- author_url
- https://medium.com/@yihengwu
- status
- ok
- fetched_at
- 2026-08-09 03:41:20