JavaScript Design Patterns

Difficulty: Intermediate

Question

What are common design patterns used in JavaScript? Give examples of each.

Answer

Key JavaScript design patterns:

Observer/PubSub: objects subscribe to events, decoupling publishers from subscribers. Foundation of event emitters and reactive programming.

Singleton: ensures only one instance of a class exists. Common for stores, loggers, database connections.

Factory: creates objects without exposing instantiation logic. Useful when the type of object varies based on conditions.

Strategy: defines interchangeable algorithms behind a common interface. Useful for sorting strategies, payment methods.

Module: encapsulates private state using closures or ES modules.

Code examples

Observer/EventEmitter Pattern

class EventEmitter {
  constructor() { this._events = {}; }

  on(event, listener) {
    if (!this._events[event]) this._events[event] = [];
    this._events[event].push(listener);
    return this;
  }

  off(event, listener) {
    this._events[event] = (this._events[event] || [])
      .filter(l => l !== listener);
    return this;
  }

  emit(event, ...args) {
    (this._events[event] || []).forEach(l => l(...args));
  }

  once(event, listener) {
    const wrapper = (...args) => {
      listener(...args);
      this.off(event, wrapper);
    };
    return this.on(event, wrapper);
  }
}

const emitter = new EventEmitter();
emitter.on('data', (msg) => console.log('Received:', msg));
emitter.once('connect', () => console.log('Connected!'));

emitter.emit('connect');
emitter.emit('connect');
emitter.emit('data', 'Hello');

The Observer pattern decouples event producers from consumers. once() auto-removes after first call.

Singleton and Factory Patterns

const Logger = (() => {
  let instance;
  return {
    getInstance() {
      if (!instance) {
        instance = {
          logs: [],
          log(msg) {
            this.logs.push(msg);
            console.log(`[LOG] ${msg}`);
          }
        };
      }
      return instance;
    }
  };
})();

const l1 = Logger.getInstance();
const l2 = Logger.getInstance();
console.log(l1 === l2);
l1.log('App started');
console.log(l2.logs);

function createAnimal(type) {
  const animals = {
    dog: () => ({ sound: () => 'Woof', legs: 4 }),
    bird: () => ({ sound: () => 'Tweet', legs: 2 })
  };
  if (!animals[type]) throw new Error(`Unknown type: ${type}`);
  return animals[type]();
}

console.log(createAnimal('dog').sound());
console.log(createAnimal('bird').legs);

Singleton ensures one instance via closure. Factory returns different implementations based on a parameter.

Key points

Concepts covered

Observer, Singleton, Factory, Strategy, Module