JS Design Patterns Quickstart

Today

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