Skip to Content
Course content

160: The Revealing Module Pattern

Click on the "Edit" button in the top corner of the screen to edit your slide content.

We've talked about the basic Module Pattern before, but there's a slight variation that I find myself using much more often in real-world production code: the Revealing Module Pattern. The core goal is the same—encapsulation—but the way we structure the "public" part of the module is different and, frankly, a lot cleaner to maintain.

Think of it this way: instead of defining functions directly inside the returned object, we define everything as private functions first, and then we simply "reveal" the ones we want the rest of the app to see. Let's build a simple CartManager to track items in a shopping cart without letting the rest of the script mess with the underlying array.

Hiding the messy bits

First, I'll wrap everything in an IIFE (Immediately Invoked Function Expression). This creates a private scope. I'll start by declaring my private state—the items array—and a helper function to format the currency. Since these are defined inside the function, they are completely inaccessible from the outside.

const CartManager = (function() {
  // Private state
  const items = [];

  // Private helper
  const formatPrice = (price) => `$${price.toFixed(2)}`;
  
  // We'll add logic here...
})();

Defining our internal logic

Now, I'll write the actual functionality. I want to be able to add items and get the total. I'm writing these as standalone functions within the closure. Notice how addItem can access the items array because they share the same scope.

const CartManager = (function() {
  const items = [];
  const formatPrice = (price) => `$${price.toFixed(2)}`;

  function addItem(name, price) {
    items.push({ name, price });
    console.log(`${name} added to cart.`);
  }

  function getTotal() {
    const total = items.reduce((sum, item) => sum + item.price, 0);
    return formatPrice(total);
  }

  function clearCart() {
    items.length = 0;
  }
  
  // Now we need to reveal them...
})();

The "Wait, why did I do that?" moment

Here is where I usually make a rookie mistake when I'm rushing. I might try to return the internal state just "in case" I need to debug it later. Let's see what happens if I do that.

// WRONG WAY
return {
  addItem: addItem,
  getTotal: getTotal,
  items: items // I'm exposing the private array!
};

Wait. If I return items, I've just destroyed the whole point of the pattern. Any other piece of code could now call CartManager.items.length = 0 or push garbage data into my array, bypassing my addItem logic. I need to delete that line immediately. The goal is to reveal behavior (functions), not state (data).

Tying it all together

To finish this, I'll return an object that maps public keys to my private functions. I'll use the ES6 shorthand here because addItem: addItem is just redundant.

const CartManager = (function() {
  const items = [];
  const formatPrice = (price) => `$${price.toFixed(2)}`;

  function addItem(name, price) {
    items.push({ name, price });
  }

  function getTotal() {
    const total = items.reduce((sum, item) => sum + item.price, 0);
    return formatPrice(total);
  }

  function clearCart() {
    items.length = 0;
  }

  // We reveal only the functions we want public
  return {
    addItem,
    getTotal,
    clearCart
  };
})();

// Usage:
CartManager.addItem('Mechanical Keyboard', 120);
CartManager.addItem('Gaming Mouse', 60);
console.log(CartManager.getTotal()); // "$180.00"
console.log(CartManager.items); // undefined (Success! It's private)

This pattern is great because if I decide later that clearCart should be internal-only, I don't have to hunt through my code to change how the function is defined; I just remove it from the return object.




📋 Practical Task

Build a Private Game Score Tracker

Your task is to create a ScoreTracker module using the Revealing Module Pattern. The module must meet the following requirements:

  • Maintain a private score variable (starting at 0).
  • Include a private function logChange(amount) that console logs "Score changed by: [amount]".
  • Reveal a public function increaseScore(points) that adds points to the score and calls the private logChange function.
  • Reveal a public function decreaseScore(points) that subtracts points and calls the private logChange function.
  • Reveal a public function getCurrentScore() that returns the current score.
  • Ensure that the score variable itself is not accessible from outside the module.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.