🏛️

Common JavaScript Design Patterns

JavaScriptAdvanced

Reusable solutions to common problems.

Code Example

JavaScript
// 1. Module Pattern
const Module = (function() {
  let privateVar = 'secret';
  
  function privateMethod() {
    console.log('Private');
  }
  
  return {
    publicMethod() {
      console.log('Public');
      privateMethod();
    },
    getPrivate() {
      return privateVar;
    }
  };
})();

Module.publicMethod();

// 2. Singleton Pattern
class Singleton {
  constructor() {
    if (Singleton.instance) {
      return Singleton.instance;
    }
    Singleton.instance = this;
    this.data = [];
  }
  
  addData(item) {
    this.data.push(item);
  }
}

const instance1 = new Singleton();
const instance2 = new Singleton();
console.log(instance1 === instance2); // true

// 3. Factory Pattern
class User {
  constructor(name, role) {
    this.name = name;
    this.role = role;
  }
}

class UserFactory {
  static createUser(name, role) {
    return new User(name, role);
  }
}

const user = UserFactory.createUser('Alice', 'admin');

// 4. Observer Pattern
class EventEmitter {
  constructor() {
    this.events = {};
  }
  
  on(event, callback) {
    if (!this.events[event]) {
      this.events[event] = [];
    }
    this.events[event].push(callback);
  }
  
  emit(event, data) {
    if (this.events[event]) {
      this.events[event].forEach(cb => cb(data));
    }
  }
  
  off(event, callback) {
    if (this.events[event]) {
      this.events[event] = this.events[event]
        .filter(cb => cb !== callback);
    }
  }
}

const emitter = new EventEmitter();
emitter.on('data', (data) => console.log(data));
emitter.emit('data', 'Hello');

// 5. Strategy Pattern
class ShoppingCart {
  constructor() {
    this.items = [];
  }
  
  setPaymentStrategy(strategy) {
    this.paymentStrategy = strategy;
  }
  
  checkout() {
    const total = this.items.reduce((sum, item) => 
      sum + item.price, 0);
    return this.paymentStrategy.pay(total);
  }
}

class CreditCardPayment {
  pay(amount) {
    console.log(`Paid ${amount} with credit card`);
  }
}

class PayPalPayment {
  pay(amount) {
    console.log(`Paid ${amount} with PayPal`);
  }
}

const cart = new ShoppingCart();
cart.setPaymentStrategy(new CreditCardPayment());
cart.checkout();

// 6. Decorator Pattern
function logged(target, key, descriptor) {
  const original = descriptor.value;
  
  descriptor.value = function(...args) {
    console.log(`Calling ${key} with`, args);
    const result = original.apply(this, args);
    console.log(`Result:`, result);
    return result;
  };
  
  return descriptor;
}

class Calculator {
  @logged
  add(a, b) {
    return a + b;
  }
}

// 7. Command Pattern
class Command {
  execute() {}
  undo() {}
}

class LightOnCommand extends Command {
  constructor(light) {
    super();
    this.light = light;
  }
  
  execute() {
    this.light.on();
  }
  
  undo() {
    this.light.off();
  }
}

class Light {
  on() { console.log('Light on'); }
  off() { console.log('Light off'); }
}

const light = new Light();
const command = new LightOnCommand(light);
command.execute();
command.undo();

// 8. Middleware Pattern
class App {
  constructor() {
    this.middleware = [];
  }
  
  use(fn) {
    this.middleware.push(fn);
  }
  
  async handle(req) {
    let index = 0;
    
    const next = async () => {
      if (index < this.middleware.length) {
        const fn = this.middleware[index++];
        await fn(req, next);
      }
    };
    
    await next();
  }
}

const app = new App();
app.use(async (req, next) => {
  console.log('Middleware 1');
  await next();
});
app.use(async (req, next) => {
  console.log('Middleware 2');
  await next();
});

Explanation

Module for encapsulation. Singleton for single instance. Factory for object creation. Observer for events. Strategy for interchangeable algorithms. Decorator for extending behavior.

Related Tutorials