The Codex / JavaScript / Design Patterns

Design Patterns

Reusable solutions to common problems — the essential JavaScript patterns: module, singleton, observer, factory, strategy, and more.

What it is, in plain English: A design pattern is a reusable solution to a commonly occurring problem in software design. JavaScript patterns range from the Module pattern (private state with closures) to the Observer pattern (the foundation of every event system and reactive framework) to the Factory pattern (creating objects without specifying their class). Knowing these patterns lets you recognise and name what experienced developers do by instinct.

Named solutions to problems you'll encounter repeatedly

Every experienced developer has solved the same problems dozens of times. Design patterns are the named, agreed-upon solutions to the most common ones. Knowing their names lets you say "use the Observer pattern here" instead of describing the whole thing from scratch.

Module pattern — private state with closures

// The module pattern creates private state — accessible only through
// the returned public interface:
function createCounter(initial = 0) {
  let count = initial;   // private — not accessible from outside

  return {
    increment() { count++; },
    decrement() { count--; },
    reset()     { count = initial; },
    value()     { return count; },
  };
}

const counter = createCounter(10);
counter.increment();
counter.increment();
console.log(counter.value());  // 12
// counter.count  → undefined (private!)

Singleton — one instance only

// Useful for: config, loggers, database connections
class AppConfig {
  static #instance = null;
  #settings = {};

  static getInstance() {
    if (!AppConfig.#instance) {
      AppConfig.#instance = new AppConfig();
    }
    return AppConfig.#instance;
  }

  set(key, value) { this.#settings[key] = value; }
  get(key)        { return this.#settings[key]; }
}

const config1 = AppConfig.getInstance();
const config2 = AppConfig.getInstance();
config1.set("theme", "dark");
console.log(config2.get("theme"));  // "dark" — same instance!
console.log(config1 === config2);   // true

Observer — subscribe to events

// The pattern behind: DOM events, Node's EventEmitter, Redux, Vue reactivity
class Store {
  #state;
  #listeners = [];

  constructor(initialState) { this.#state = initialState; }

  getState() { return { ...this.#state }; }   // return copy, not reference

  setState(updates) {
    this.#state = { ...this.#state, ...updates };
    this.#listeners.forEach(fn => fn(this.#state));  // notify all observers
  }

  subscribe(listener) {
    this.#listeners.push(listener);
    return () => {  // return unsubscribe function
      this.#listeners = this.#listeners.filter(l => l !== listener);
    };
  }
}

const store = new Store({ user: null, theme: "light" });
const unsub = store.subscribe(state => console.log("State changed:", state.theme));

store.setState({ theme: "dark" });  // logs: State changed: dark
store.setState({ user: "Alice" });  // logs: State changed: dark (user added)
unsub();  // stop listening
store.setState({ theme: "light" }); // no log — unsubscribed

Factory — create objects without specifying their class

// Instead of: new Dog(), new Cat() — use a factory:
function createAnimal(type, name) {
  const base = {
    name,
    toString() { return `${this.type} named ${this.name}`; },
  };

  const animals = {
    dog:  () => ({ ...base, type: "Dog",  speak: () => "Woof!" }),
    cat:  () => ({ ...base, type: "Cat",  speak: () => "Meow!" }),
    bird: () => ({ ...base, type: "Bird", speak: () => "Tweet!" }),
  };

  const factory = animals[type.toLowerCase()];
  if (!factory) throw new Error(`Unknown animal: ${type}`);
  return factory();
}

const dog = createAnimal("dog", "Rex");
const cat = createAnimal("cat", "Whiskers");
console.log(dog.speak());         // "Woof!"
console.log(cat.toString());      // "Cat named Whiskers"
Try It Yourself

Official Sources

The Codex links only to official documentation and the ECMAScript specification. No third-party tutorials.