Inheritance in JavaScript
Inheritance in JavaScript — extends, super, and prototype chain
In JavaScript, inheritance is based on the prototype chain
. Starting with ES6, the class syntax simplified this mechanism with the extends
and super()
keywords. In this section, we'll analyze how class inheritance actually works, including method and property inheritance, the chain connection, and the role of super
.
Let's start with a simple example where one class inherits from another using extends
and super()
.
class Animal { constructor(name) { this.name = name; }
speak() { console.log(${this.name} makes a sound); } }
class Dog extends Animal { constructor(name, breed) { super(name); // Calls the parent constructor this.breed = breed; }
speak() { super.speak(); // Calls the parent method console.log(${this.name} barks); } }
const d = new Dog('Rex', 'Labrador'); d.speak(); // Rex makes a sound // Rex barks
Explanation:
Dog extends Animal
means Dog inherits from Animal's prototype
super(name)
is mandatory if we write a constructor in the child class
super.speak()
calls the parent class's method
When we use extends
, JavaScript creates two chains: one for prototype
objects and one between constructors.
console.log(Dog.prototype.__proto__ === Animal.prototype); // true
console.log(Dog.__proto__ === Animal); // true
These connections allow an instance to access both its own methods and its parent's methods through the [[Prototype]]
chain.
Graphically:
d --> Dog.prototype --> Animal.prototype --> Object.prototype
super() - Used inside the constructor to call the parent's constructor. Must be used before accessing this
.
constructor(name) { // this.name = name; ❌ Error super(name); // ✅ }
super.method() - Used to access a parent class's method from within an instance method.
speak() { super.speak(); // Calls Animal.prototype.speak }
Static methods are also inherited. If a base class has a static method, the derived class will have it too without needing to redefine it.
class Person { static info() { return 'I am a Person'; } }
class Developer extends Person {}
console.log(Developer.info()); // I am a Person
If the same method is defined in multiple levels of classes, JavaScript uses the closest method according to the chain.
class A { who() { return 'A'; } }
class B extends A { who() { return 'B'; } }
class C extends B { who() { return super.who() + '->C'; } }
const c = new C(); console.log(c.who()); // B->C
Explanation:
super.who()
calls B's method because C directly inherits from B.
You can create "structural" inheritance without classes using Object.assign
, but this doesn't create prototype inheritance.
const animalMethods = { speak() { console.log(`${this.name} speaks`); } };
function createDog(name) { const dog = { name }; return Object.assign(dog, animalMethods); }
This approach works but doesn't support instanceof
, doesn't preserve constructor
, and is less clean for large applications.
JavaScript's class inheritance system is built on the prototype chain
, but ES6 syntax presents it as a clear OOP structure. The extends
and super()
keywords make inheritance more readable, though internally they work on the same prototype
principles.
In the next part, we'll discuss how to implement polymorphism in JavaScript classes, override methods, and create common interfaces for different classes.