๐Ÿ— Day 24 — Classes & Object-Oriented JavaScript

๐Ÿ— Day 24 — Classes & Object-Oriented JavaScript

Hey Guys Object-Oriented Programming (OOP) helps structure complex applications. Today we’ll explore ES6 classes, inheritance, prototypes, encapsulation, and practical patterns for building maintainable JavaScript code.


๐Ÿ“˜ Why OOP in JavaScript?

JavaScript is prototype-based, but ES6 `class` syntax gives a clearer, more familiar structure for building objects. OOP helps group data and behavior, enforce invariants, and create reusable components.

๐Ÿ‘ค Basic class syntax

A class encapsulates state (properties) and behavior (methods). The `constructor` is called when creating an instance with `new`.

class User {
  constructor(name, email) {
    this.name = name;
    this.email = email;
    this.created = Date.now();
  }

  greet() {
    return `Hello, ${this.name}`;
  }

  toJSON() {
    return { name: this.name, email: this.email };
  }
}

const u = new User('Rahul', 'rahul@example.com');
console.log(u.greet());
  

๐Ÿงฌ Inheritance & extends

Use `extends` to build specialized classes. Always call `super()` in the subclass constructor to initialize the base class.

class Admin extends User {
  constructor(name, email, level = 1) {
    super(name, email);
    this.level = level;
  }

  canDelete() {
    return this.level > 0;
  }
}

const a = new Admin('Manager', 'mgr@example.com', 2);
console.log(a.canDelete()); // true
  

๐Ÿ” Prototypes under the hood

Classes are syntactic sugar over prototype-based inheritance. `User.prototype` contains methods shared by instances.

console.log(Object.getPrototypeOf(u) === User.prototype); // true
  

๐Ÿ”’ Encapsulation: private fields & methods

Modern JS supports private fields (`#`) for encapsulation and `get`/`set` for controlled access.

class BankAccount {
  #balance = 0;

  constructor(initial = 0) {
    this.#balance = initial;
  }

  deposit(amount) {
    if (amount <= 0) throw new Error('Amount must be positive');
    this.#balance += amount;
  }

  get balance() { return this.#balance; }
}
  

⚙ Composition over inheritance

Prefer composition when behavior can be assembled from smaller parts. It often results in more flexible and testable code than deep inheritance hierarchies.

function withLogging(obj) {
  return new Proxy(obj, {
    get(target, prop, receiver) {
      const v = Reflect.get(target, prop, receiver);
      if (typeof v === 'function') {
        return function(...args) {
          console.log(`Calling ${String(prop)}`, args);
          return v.apply(this, args);
        };
      }
      return v;
    }
  });
}
  

๐Ÿงช Patterns: Factory, Singleton, Module

- Factory: function that returns configured objects. - Singleton: single shared instance (use sparingly). - Module: encapsulates state and exports behavior (ES modules).

๐Ÿ“ Practical example: Component class

Basic UI component pattern using classes — each instance manages its DOM root and lifecycle.

class Counter {
  constructor(root) {
    this.root = root;
    this.count = 0;
    this.render();
  }

  increment() {
    this.count++;
    this.update();
  }

  render() {
    this.root.innerHTML = `
      
${this.count}
`; this.root.querySelector('.inc').addEventListener('click', () => this.increment()); } update() { this.root.querySelector('.value').textContent = this.count; } }

๐Ÿ“ Best practices

  • Favor small classes focused on a single responsibility.
  • Use composition to add behavior dynamically.
  • Keep state immutable where possible — makes reasoning easier.
  • Write unit tests for class behavior (constructor, methods).

๐Ÿงพ Migration tips from prototypes to classes

  1. Map prototype functions to class methods.
  2. Move shared behavior onto prototypes (or class methods) rather than on instances.
  3. Test before refactor, then swap to `class` syntax for readability.

๐Ÿ“ Practice tasks (real-world)

  1. Build a `Modal` class that controls open/close and focus trapping.
  2. Create a `Store` class for app state with subscribe/publish methods.
  3. Refactor an existing function-based module into a class-based module and write tests.

๐ŸŽฏ Summary

ES6 classes give you a clean, familiar syntax for building structured JavaScript. Understand the prototype roots, use private fields for encapsulation, prefer composition when possible, and keep classes single-responsibility for maintainable code.

Comments