Classical Inheritance in JavaScript

Hard

While JavaScript uses prototypal inheritance, you can implement classical inheritance patterns. Before ES6 classes, this was done with constructor functions and Object.create(). Understanding these patterns helps you understand how extends works under the hood and how to work with older codebases.

Interactive Visualization

Code
1class Animal {
2 constructor(name) {
3 this.name = name
4 }
5 speak() {
6 console.log(`${this.name} makes a sound`)
7 }
8}
Prototype Chain
Chain will appear after class definition
Define the parent class Animal with a constructor and method
1 / 4
Key Insight: extends sets up the prototype chain (Dog.prototype.__proto__ = Animal.prototype). super() calls the parent constructor to initialize inherited properties.

Key Points

  • Subclass.prototype = Object.create(Superclass.prototype)
  • Call parent constructor: Superclass.call(this, args)
  • Fix constructor property after setting prototype
  • ES6 extends handles this automatically
  • Understanding the pattern helps debug older code

Code Examples

Manual Inheritance Pattern

function Animal(name) {
  this.name = name;
}

Animal.prototype.speak = function() {
  return this.name + " makes a sound";
};

// Dog inherits from Animal
function Dog(name, breed) {
  // Call parent constructor
  Animal.call(this, name);
  this.breed = breed;
}

// Set up prototype chain
Dog.prototype = Object.create(Animal.prototype);

// Fix constructor property
Dog.prototype.constructor = Dog;

// Add Dog-specific method
Dog.prototype.speak = function() {
  return this.name + " barks";
};

const dog = new Dog("Rex", "German Shepherd");
console.log(dog.speak());  // "Rex barks"
console.log(dog instanceof Dog);     // true
console.log(dog instanceof Animal);  // true

Manual inheritance: call parent constructor, set prototype with Object.create(), fix constructor.

ES6 Equivalent

class Animal {
  constructor(name) {
    this.name = name;
  }
  
  speak() {
    return this.name + " makes a sound";
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name);  // Calls parent constructor
    this.breed = breed;
  }
  
  speak() {
    return this.name + " barks";
  }
}

const dog = new Dog("Rex", "German Shepherd");

// extends desugars to the manual pattern above
// super() desugars to Animal.call(this, name)

ES6 class extends handles all the manual prototype setup automatically.

Common Mistakes

  • Forgetting to call parent constructor
  • Not setting Dog.prototype.constructor back to Dog
  • Using Dog.prototype = new Animal() instead of Object.create()

Interview Tips

  • Know the manual inheritance pattern
  • Understand what extends desugars to
  • Know why Object.create() is preferred over new Parent()
  • Be able to set up inheritance without class syntax