Design Patterns in JavaScript
Design Patterns in JavaScript
Classic creational, structural, and behavioral patterns tailored for modern idiomatic JavaScript.
The Master Architect Catalog
Design patterns provide battle-tested solutions to recurring software architecture challenges. In JavaScript, patterns emphasize composition, closures, and first-class functions over rigid class inheritance.
Creational: Singleton & Factory
Singleton ensures a single shared instance (ES modules are natural singletons). Factory abstracts object instantiation logic based on input parameters.
Structural: Adapter, Decorator, and Proxy
Adapter maps incompatible interfaces. Decorator wraps objects to add behaviors dynamically. Proxy intercepts operations for validation or caching.
Behavioral: Observer / Pub-Sub
Establishes a one-to-many dependency where publisher emits events and multiple subscribers react without tight coupling.
Behavioral: Strategy Pattern
Encapsulates interchangeable algorithms (e.g., payment strategies: CreditCard, PayPal, Crypto) behind a unified execution interface.
Implementing a production-grade Pub-Sub (EventEmitter) pattern:
class EventEmitter {
constructor() {
this.events = new Map();
}
subscribe(eventName, callback) {
if (!this.events.has(eventName)) {
this.events.set(eventName, new Set());
}
const handlers = this.events.get(eventName);
handlers.add(callback);
// Return an unsubscribe function
return () => handlers.delete(callback);
}
emit(eventName, ...args) {
const handlers = this.events.get(eventName);
if (handlers) {
handlers.forEach(fn => fn(...args));
}
}
}
// Practical decoupled usage
const bus = new EventEmitter();
const unsubscribe = bus.subscribe("ORDER_PLACED", (order) => {
console.log(`[Email Service] Sending receipt to: ${order.email}`);
});
bus.subscribe("ORDER_PLACED", (order) => {
console.log(`[Analytics] Recording sale of $${order.total}`);
});
bus.emit("ORDER_PLACED", { email: "dev@consistcode.com", total: 99 });
[Email Service] Sending receipt to: dev@consistcode.com
[Analytics] Recording sale of $99
Over-Engineering with Rigid Patterns
The Risk: Applying complex OOP patterns (like Abstract Factory or Visitor) where a simple pure function or object literal would suffice.
The Fix: Keep patterns pragmatic. JavaScript's first-class functions often replace entire OOP pattern hierarchies with one-liners.
- ✓ Pub-Sub / Observer decouples event producers from consumers.
- ✓ Singleton: Exactly one instance per runtime (ES modules are singletons by default).
- ✓ Factory: Encapsulates instantiation complexity behind a clean function.
- ✓ Strategy: Passes interchangeable algorithmic functions as arguments.
- ✓ Favor functional composition over heavy classical inheritance.