← Back to list

JavaScript Design Pattern 筆記 — Module/Revealing Module

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

YiHeng Wu · 2024-11-14 15:41 · 0 claps · 8.7 min read
#javascript-design-pattern #javascript #module-pattern #revealing-module-pattern #garbage-collection
Open on Medium ↗
Wiki topics: 🌐 · Web Development

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 中,可以透過引入 MixinWeakMap 來擴展 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)會使用標記-清除的變體,稱為分代垃圾回收,將記憶體分為兩代:

  1. 新生代:短期物件,創建並很快變得無用(例如局部變數)。
  2. 老生代:存活更久的物件。

在此機制中,新生代的垃圾回收比較頻繁,而老生代的回收則較少發生,這樣可以提高整體回收效率。

垃圾回收的優勢和限制

  • 優勢:簡化記憶體管理,自動釋放無用資源,防止記憶體洩漏。
  • 限制:回收過程中可能導致應用程式暫時停止;且對大型應用程式來說,垃圾回收的處理時間會隨著記憶體佔用量增大而增加。

垃圾回收機制讓開發人員無需手動釋放記憶體,極大地提升了高階語言的開發效率,但同時也需要理解如何有效管理變數範疇和引用,避免因不當引用而導致的記憶體洩漏問題。


메타데이터
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