JavaScript Design Patterns: Quickstart Guide
Design patterns provide reusable solutions to common problems in software design. In JavaScript, they help create maintainable, scalable code. This guide focuses on four essentials: Singleton, Factory, Module, and Observer. We'll use ES6+ syntax for brevity.
Singleton Pattern
The Singleton ensures a class has only one instance and provides global access to it. Useful for managers like config or logger.
class Singleton {
constructor() {
if (Singleton.instance) return Singleton.instance;
Singleton.instance = this;
}
getInstance() {
return Singleton.instance;
}
}
const s1 = new Singleton();
const s2 = new Singleton();
console.log(s1 === s2); // true
Factory Pattern
The Factory creates objects without specifying the exact class. Ideal for object creation based on conditions.
class Car {
constructor(type) {
this.type = type;
}
start() {
console.log(`${this.type} car started`);
}
}
class Bike {
constructor(type) {
this.type = type;
}
start() {
console.log(`${this.type} bike started`);
}
}
function VehicleFactory(type) {
switch (type) {
case 'car': return new Car('sports');
case 'bike': return new Bike('mountain');
default: throw new Error('Unknown type');
}
}
const vehicle = VehicleFactory('car');
vehicle.start(); // sports car started
Module Pattern
The Module encapsulates code with private and public scopes, promoting data privacy. Leverages closures.
const Calculator = (function() {
let total = 0; // private
function add(n) {
total += n;
}
function subtract(n) {
total -= n;
}
return {
add, // public
subtract, // public
getTotal: () => total // read-only public
};
})();
Calculator.add(5);
Calculator.subtract(2);
console.log(Calculator.getTotal()); // 3
console.log(Calculator.total); // undefined (private)
Observer Pattern
The Observer defines a one-to-many dependency where subjects notify observers of state changes. Great for event handling.
class Subject {
constructor() {
this.observers = [];
}
addObserver(observer) {
this.observers.push(observer);
}
removeObserver(observer) {
this.observers = this.observers.filter(obs => obs !== observer);
}
notify(data) {
this.observers.forEach(observer => observer.update(data));
}
setState(state) {
this.state = state;
this.notify(state);
}
}
class Observer {
constructor(name) {
this.name = name;
}
update(data) {
console.log(`${this.name} received: ${data}`);
}
}
const subject = new Subject();
const obs1 = new Observer('User1');
const obs2 = new Observer('User2');
subject.addObserver(obs1);
subject.addObserver(obs2);
subject.setState('New alert!');
// User1 received: New alert!
// User2 received: New alert!
Wrapping Up
These patterns—Singleton for uniqueness, Factory for creation, Module for encapsulation, and Observer for notifications—form the foundation of robust JS architecture. Practice by refactoring small projects. For deeper dives, explore books like "JavaScript Design Patterns" by Addy Osmani. Total chars: ~2450