JavaScript Classes & Inheritance

Medium100% Free~25 mins#classes#inheritance#extends#super#static-methods#private-fields#prototypes#oop
Key Learning Objectives
✓

Understand ES6 class declarations as syntactic sugar over prototypal inheritance, including strict mode enforcement and temporal dead zone (TDZ).

✓

Master instance methods vs static methods and understand how JavaScript inherits static methods via constructor prototypes.

✓

Learn the mechanics of extends, super(), and constructor initialization order.

✓

Differentiate public fields, private fields (#), and methods on the prototype chain.

✓

Understand why class methods are non-enumerable on prototypes and how instanceof traverses the prototype chain.

The Interview Problem

What is logged to the console when the following class hierarchy executes, and how do static inheritance, private fields, and prototype enumerability operate?

1class Vehicle {
2 #fuel = 100;
3 static count = 0;
4
5 constructor(brand) {
6 this.brand = brand;
7 Vehicle.count++;
8 }
9
10 getFuel() {
11 return this.#fuel;
12 }
13
14 static getCount() {
15 return this.count;
16 }
17}
18
19class Car extends Vehicle {
20 constructor(brand, doors = 4) {
21 super(brand);
22 this.doors = doors;
23 }
24
25 getDetails() {
26 return this.brand + '-' + this.doors + '-' + this.getFuel();
27 }
28}
29
30const c1 = new Car('Tesla', 2);
31const res1 = c1.getDetails();
32const res2 = Car.getCount();
33const res3 = Object.keys(Car.prototype).length;
34const res4 = c1 instanceof Vehicle;
35
36console.log(res1, res2, res3, res4);
Predict Console Output
Interactive Challenge

Select the option that matches what standard ECMAScript prints to the console:

Tesla-2-100 1 0 true

Tesla-2-undefined 0 1 true

Tesla-2-100 1 1 false

Tesla-4-100 1 0 true

V8 Engine Execution Trace
Step 1 of 6 (Line 1)

Classes are evaluated. Vehicle constructor is created. Car constructor is created with prototype linkage: Car.prototype.__proto__ === Vehicle.prototype and Car.__proto__ === Vehicle.

Call Stack (Top = Active)
Global Execution Context
Lexical Scope / Bindings
Vehicle:class Vehicle
Car:class Car extends Vehicle
Console Stream
> [empty]

Deep Technical Breakdown

What Are JavaScript Classes Under the Hood?

ES6 classes are syntactic sugar over JavaScript's prototype-based object model, but they introduce important engine semantics:

  1. Mandatory new: Invoking a class without new throws TypeError: Class constructor cannot be invoked without 'new'.
  2. No Hoisting (TDZ): Unlike function declarations, class declarations are not hoisted with initialization; accessing them before their declaration line throws a ReferenceError.
  3. Automatic Strict Mode: The entire body of a class runs in 'use strict' by default.
  4. Non-Enumerable Prototype Methods: Methods declared in a class body (getDetails() {}) are added to Class.prototype with descriptor { enumerable: false, writable: true, configurable: true }. This is why Object.keys(Car.prototype) returns [].

The Dual Prototype Chain of extends

When class Subclass extends Superclass is declared, the engine sets up two distinct prototype links:

javascript
// 1. Instance method chain:
Subclass.prototype.__proto__ === Superclass.prototype; // true

// 2. Static method chain:
Subclass.__proto__ === Superclass; // true

Because Subclass.__proto__ points to Superclass, calling Subclass.staticMethod() automatically finds methods declared static on Superclass!

Constructor and super() Restrictions

In a derived class constructor:

  • You must call super(...args) before referencing this.
  • The engine allocates this during the superclass constructor execution. Accessing this before super() throws ReferenceError: Must call super constructor in derived class before accessing 'this'.

Private Class Fields (#)

  • Declared with a # prefix (e.g. #fuel = 100).
  • Truly private at the engine level: cannot be accessed from outside the class body, not visible in Object.getOwnPropertyNames(), Reflect.ownKeys(), or proxy traps.
  • Subclasses cannot directly access private fields of their parent (this.#fuel in Car would throw a syntax error). However, subclasses can invoke inherited parent methods (getFuel()) that read the private field.
Common Traps & Mistakes

Accessing `this` before calling `super()` in a derived class constructor, which throws an immediate `ReferenceError`.

Assuming static methods are copied to instances. Static methods exist on the constructor function itself, not on instance objects (`c1.getCount` is `undefined`).

Expecting `Object.keys(Class.prototype)` to list the class's methods. Class methods are strictly non-enumerable by specification.

Attempting to access a superclass private field (`this.#fuel`) directly in a subclass body, which throws a syntax error.

FAANG Follow-Up Probes
Probe #1

What happens if you return an object from a constructor? Does `new MyClass()` return `this` or the returned object?

Probe #2

How does `super.method()` resolve in an overridden method, and what does `super` bind to lexically?

Probe #3

How would you replicate the static inheritance of `class Car extends Vehicle` using ES5 functions and `Object.setPrototypeOf`?