Topics in this subject
JavaScript 3 min read Updated 5 Aug 2026

Object-Oriented JavaScript

Static, private, and checks, Composition vs inheritance & mixins 🎯, SOLID in JS (pragmatic)

Classes are syntactic sugar over prototypes (Objects). Under the hood, methods live on Class.prototype.

class Animal {
  static count = 0;                  // static field (on the class)
  #energy = 100;                     // private field (truly private) 🟢

  constructor(name) {
    this.name = name;                // public instance field
    Animal.count++;
  }
  speak() { return `${this.name} speaks`; }   // on prototype
  get energy() { return this.#energy; }        // getter
  set energy(v) { this.#energy = Math.max(0, v); }
  #recover() { this.#energy += 10; }           // private method
  static create(name) { return new Animal(name); } // static method
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name);                     // must call before using `this`
    this.breed = breed;
  }
  speak() { return `${super.speak()} — woof`; } // override + super
}

const d = new Dog("Rex", "Lab");
d.speak();          // "Rex speaks — woof"
d instanceof Animal;// true
Animal.count;       // 1
// d.#energy         // ❌ SyntaxError — private, inaccessible outside

Static, private, and checks#

  • static members belong to the class, not instances.
  • #field / #method() are hard private — enforced by the engine (unlike _name convention).
  • #x in obj 🟢 (ES2022) is the brand check for private fields.

Composition vs inheritance & mixins 🎯#

Prefer composition ("has-a") over deep inheritance ("is-a"). Deep hierarchies are rigid and fragile.

// mixin — share behaviour without inheritance
const Serializable = (Base) => class extends Base {
  toJSON() { return JSON.stringify({ ...this }); }
};
class User {}
class SerializableUser extends Serializable(User) {}

SOLID in JS (pragmatic)#

  • Single Responsibility — a module/class does one thing.
  • Open/Closed — extend behaviour without editing existing code (strategy pattern, plugins).
  • Liskov — subtypes must be usable wherever the base is.
  • Interface Segregation — small focused interfaces (in JS, small focused objects/functions).
  • Dependency Inversion — depend on abstractions; inject dependencies rather than hard-coding them.

Interview Q&A#

Q1. Are JS classes "real" classes? What's underneath? No — class is syntactic sugar over prototypes. Methods live on Class.prototype, and extends wires up the prototype chain. There's no separate class type system as in Java/C++.

Q2. #private vs _convention privacy — what's the difference and how is it enforced? #field/#method() are hard private, enforced by the engine — accessing them from outside is a SyntaxError and they're invisible to reflection. _name is only a naming convention; nothing stops outside code from reading or writing it.

Q3. Composition vs inheritance — when to prefer each? Prefer composition ("has-a") for most cases: it stays flexible and avoids the rigidity of deep hierarchies. Reach for inheritance only for genuine "is-a" relationships that are shallow and stable; mixins let you share behaviour without a rigid chain.

Q4. What does super do and when must you call it? super(...) invokes the parent constructor and super.method() calls the parent's method (enabling override + delegation). In a subclass constructor you must call super() before touching this, otherwise you get a ReferenceError.