JavaScript
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax and Types
-
Section 3: Strings and Numbers in Depth
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Objects and Arrays
-
Section 7: Maps, Sets, and Symbols
-
Section 8: Asynchronous JavaScript
-
Section 9: Object-Oriented and Prototypes
-
Section 10: The DOM
-
Section 11: Browser APIs
-
Section 12: Modern JavaScript (ES2015-ES2025)
-
Section 13: Functional Programming Patterns
-
Section 14: Error Handling and Debugging
-
Section 15: Testing
-
Section 16: Accessibility for JavaScript Developers
-
Section 17: Internationalization and Localization
-
Section 18: Performance
-
Section 19: Node.js Fundamentals
-
Section 20: Regular Expressions
-
Section 21: Design Patterns in JavaScript
-
Section 22: Security Basics
-
Section 23: Data Structures and Algorithms in JavaScript
-
Section 24: Practical Projects
-
Section 25: More Advanced Async Patterns
-
Section 26: More Object and Class Practice
-
Section 27: Working with Dates and Internationalization
-
Section 28: Web Components
-
Section 29: More DOM and Browser Practice
-
Section 30: Build Tooling for Vanilla JavaScript
-
Section 31: More Practice Projects
-
Section 32: Interview and Algorithm Practice
-
Section 33: Error Objects (MDN Reference)
-
Section 34: TypedArrays and Binary Data
-
Section 35: Reflection and Metaprogramming (MDN Reference)
-
Section 36: More Global Functions (MDN Reference)
160: The Revealing Module Pattern
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
scorevariable (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 privatelogChangefunction. - Reveal a public function
decreaseScore(points)that subtracts points and calls the privatelogChangefunction. - Reveal a public function
getCurrentScore()that returns the current score. - Ensure that the
scorevariable itself is not accessible from outside the module.
There are no comments for now.