10 questions foundThe Module pattern uses a function closure (or an ES module) to keep internal variables private and expose only a controlled public API, preventing external code from directly manipulating internal state.
const counter = (function () {
let count = 0;
return {
increment: () => ++count,
get: () => count
};
})();
counter.increment();
console.log(counter.get()); // 1
Real-world example Building a small state-tracking utility without leaking its internal variable to the global scope.
Common follow-ups: How do native ES modules make the classic IIFE module pattern less necessary today?
Scope Hoisting & Closures
Singleton ensures only one instance of an object exists across the whole application, typically by exporting a single already-created instance from a module — since ES modules are cached, importing it anywhere returns the same instance.
// config.js
class Config {
constructor() { this.settings = {}; }
}
export default new Config(); // same instance everywhere it's imported
Real-world example A single shared application configuration or logger instance used across many files.
Common follow-ups: What are the downsides of overusing Singletons, especially for testing?
ES Modules
Maintain a list of subscriber callback functions; provide a subscribe() method to add them and a notify() (or emit()) method that loops through and calls each one when an event occurs — this decouples the event source from its consumers.
class EventEmitter {
#listeners = [];
subscribe(fn) { this.#listeners.push(fn); }
emit(data) { this.#listeners.forEach(fn => fn(data)); }
}
Real-world example Powering a custom pub-sub system for decoupled communication between UI components.
Common follow-ups: How does Node's built-in EventEmitter class relate to this pattern?
Classes & Class Syntax
A factory is a function that encapsulates the logic for deciding which class/object to instantiate and how, hiding that complexity from the caller — useful when construction logic is conditional or needs to vary at runtime.
function createShape(type) {
switch (type) {
case 'circle': return new Circle();
case 'square': return new Square();
default: throw new Error('Unknown shape');
}
}
Real-world example A UI library's createElement() function that returns different component instances based on a type string.
Common follow-ups: When would you prefer a factory function over exposing the classes directly?
Classes & Class Syntax
By wrapping a function or object with another function that adds behavior before/after calling the original, while preserving its original interface — often done with higher-order functions rather than the class decorator proposal.
function withLogging(fn) {
return (...args) => {
console.log('calling with', args);
return fn(...args);
};
}
const loggedAdd = withLogging((a, b) => a + b);
Real-world example Wrapping an API call function to automatically add logging or retry behavior without modifying the original function.
Common follow-ups: How does this differ from the experimental @decorator class syntax?
Functional Programming
Strategy defines a family of interchangeable algorithms/behaviors as separate functions or objects, and lets you swap which one is used at runtime, replacing sprawling conditional logic with a simple lookup or injected dependency.
const strategies = {
card: (amt) => payWithCard(amt),
paypal: (amt) => payWithPaypal(amt),
};
function pay(method, amount) {
return strategies[method](amount);
}
Real-world example Supporting multiple payment or shipping calculation methods that can be swapped without touching the core checkout logic.
Common follow-ups: How does Strategy relate to dependency injection?
Design Patterns in JavaScript
JavaScript's native Proxy wraps a target object with traps (get, set, has, etc.) that intercept and can customize fundamental operations on it, letting you add validation, logging, or virtual properties transparently.
const validated = new Proxy({}, {
set(target, prop, value) {
if (prop === 'age' && value < 0) throw new Error('Invalid age');
target[prop] = value;
return true;
}
});
validated.age = -5; // throws
Real-world example Building a reactive state object (like Vue 3's reactivity system) that tracks property reads and writes.
Common follow-ups: What performance cost does wrapping objects in Proxy typically add?
Proxy & Reflect
Command encapsulates an action (and its parameters) as an object with a consistent execute() method, so actions can be queued, logged, undone, or passed around independently of the code that triggers them.
class AddItemCommand {
constructor(cart, item) { this.cart = cart; this.item = item; }
execute() { this.cart.push(this.item); }
undo() { this.cart.pop(); }
}
Real-world example Implementing undo/redo functionality in a drawing or text-editing application.
Common follow-ups: How does Command relate to Redux-style actions and reducers?
Functional Programming
Adapter wraps an incompatible interface with a new one that matches what your code expects, letting you swap or update the underlying library without rewriting all the code that depends on it.
class LegacyLogger {
logMessage(msg) { console.log('LEGACY:', msg); }
}
class LoggerAdapter {
#legacy = new LegacyLogger();
log(msg) { this.#legacy.logMessage(msg); } // matches modern interface
}
Real-world example Wrapping an old third-party analytics SDK so the rest of the app can call a consistent, modern log() API.
Common follow-ups: How does Adapter differ from Facade, which also 'wraps' something?
Classes & Class Syntax
Many GoF patterns exist to work around limitations of statically-typed, class-heavy languages. JavaScript's first-class functions and closures often solve the same problem more simply — e.g., a full class-based Strategy pattern is often overkill when a plain object of functions or even a single higher-order function would do.
// Overkill class-based Strategy:
class AddStrategy { execute(a,b) { return a+b; } }
// Idiomatic JS equivalent:
const add = (a, b) => a + b;
Real-world example Recognizing when a Java-style pattern is being copy-pasted into JS unnecessarily, adding ceremony without benefit.
Common follow-ups: What JavaScript language features most commonly replace the need for classic OOP patterns?
Functional Programming