JavaScript inheritance is a feature that allows one object or class to reuse properties and methods from another. It helps you organize related objects, reduce repeated code, and build reusable structures. JavaScript uses prototypal inheritance: objects can inherit behavior from other objects through a prototype chain. Modern JavaScript also provides the class and extends syntax to make inheritance easier to write and understand.

What Is Inheritance in JavaScript?

Inheritance means that an object can use properties and methods defined on another object. The object that provides the shared behavior is often called the parent or base object, while the object that receives it is called the child or derived object.

Understanding the JavaScript Prototype Chain

Every ordinary JavaScript object has an internal link to another object called its prototype, or it has null as its prototype. When you access a property, JavaScript first checks the object itself. If the property is not found, it looks on the object’s prototype, then on that prototype’s prototype, and continues until it finds the property or reaches the end of the chain.

const animal = {
  eat() {
    return "This animal is eating.";
  }
};

const dog = {
  bark() {
    return "Woof!";
  }
};

Object.setPrototypeOf(dog, animal);

console.log(dog.bark()); // "Woof!"
console.log(dog.eat());  // "This animal is eating."

The dog object owns the bark() method. It does not own eat(); JavaScript finds that method on its prototype, animal. This lookup process is called the prototype chain.

Object.setPrototypeOf() is used here to make the relationship easy to see. In application code, it is generally better to establish an object’s prototype when creating it, rather than changing the prototype of an existing object repeatedly.

Creating Inheritance with Object.create()

The Object.create() method creates a new object using the object you provide as its prototype. This is a direct way to create an object that inherits from another object.

const person = {
  greet() {
    return "Hello, my name is " + this.name;
  }
};

const student = Object.create(person);

student.name = "Rahim";
student.study = function () {
  return this.name + " is studying JavaScript.";
};

console.log(student.greet()); // "Hello, my name is Rahim"
console.log(student.study()); // "Rahim is studying JavaScript."

In this example, student inherits the greet() method from person and defines its own name and study() properties. The this value inside the inherited method refers to the object on which the method is called, which is student here.

JavaScript Inheritance Using Constructor Functions

Before ES6 classes became available, constructor functions and their prototype properties were commonly used to create reusable object types. A constructor initializes instance properties, while methods placed on its prototype are shared by instances.

Parent Constructor Function

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

Person.prototype.greet = function () {
  return "Hello, " + this.name;
};

The Person constructor creates a name property on each instance. The greet() method is stored on Person.prototype, so instances can share the same function instead of creating a separate copy for every object.

Child Constructor Function

function Student(name, course) {
  Person.call(this, name);
  this.course = course;
}

Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;

Student.prototype.study = function () {
  return this.name + " is studying " + this.course;
};

const student1 = new Student("Nadia", "JavaScript");

console.log(student1.greet()); // "Hello, Nadia"
console.log(student1.study()); // "Nadia is studying JavaScript"

There are two important steps in this example. First, Person.call(this, name) runs the parent constructor with the new student object as this, so the student receives its name property. Second, Object.create(Person.prototype) makes Student.prototype inherit from Person.prototype, allowing student instances to find the parent’s shared methods.

After replacing Student.prototype, the code restores its constructor property to Student. This is useful for keeping the conventional constructor reference accurate.

JavaScript Inheritance Using ES6 Classes

ES6 introduced class syntax, which provides a clearer way to write constructor-based inheritance. You define a parent class and use the extends keyword to create a child class.

class Person {
  constructor(name) {
    this.name = name;
  }

  greet() {
    return "Hello, " + this.name;
  }
}

class Student extends Person {
  constructor(name, course) {
    super(name);
    this.course = course;
  }

  study() {
    return this.name + " is studying " + this.course;
  }
}

const student1 = new Student("Nadia", "JavaScript");

console.log(student1.greet()); // "Hello, Nadia"
console.log(student1.study()); // "Nadia is studying JavaScript"

The Student class extends Person, so student instances inherit methods from Person.prototype. The super(name) call invokes the parent constructor and initializes the inherited name property. The child class then adds its own course property and study() method.

Understanding the super Keyword

The super keyword is used inside a derived class to access the parent class behavior. It has two common uses: calling the parent constructor with super(), and calling a parent method with super.methodName().

Calling the Parent Constructor

class Animal {
  constructor(name) {
    this.name = name;
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name);
    this.breed = breed;
  }
}

const dog1 = new Dog("Buddy", "Labrador");

console.log(dog1.name);  // "Buddy"
console.log(dog1.breed); // "Labrador"

When a derived class defines its own constructor, it must call super() before it can use this. The parent constructor initializes the inherited portion of the new instance.

Calling a Parent Method

class Animal {
  speak() {
    return "The animal makes a sound.";
  }
}

class Dog extends Animal {
  speak() {
    return super.speak() + " The dog barks.";
  }
}

const dog1 = new Dog();

console.log(dog1.speak());
// "The animal makes a sound. The dog barks."

Here, super.speak() calls the parent implementation. The child method adds its own behavior to the result instead of replacing the parent’s behavior completely.

Method Overriding in JavaScript

Method overriding happens when a child class defines a method with the same name as a method inherited from its parent. When that method is called on a child instance, the child’s implementation is used.

class Animal {
  makeSound() {
    return "Some generic animal sound";
  }
}

class Cat extends Animal {
  makeSound() {
    return "Meow";
  }
}

const animal = new Animal();
const cat = new Cat();

console.log(animal.makeSound()); // "Some generic animal sound"
console.log(cat.makeSound());    // "Meow"

The Cat class overrides makeSound() with a more specific implementation. You can still call the parent version from the child method using super.makeSound() when that behavior is needed.

Checking Inheritance with instanceof

The instanceof operator checks whether a constructor’s prototype appears in an object’s prototype chain. It is often used to test whether an object was created from a class or constructor, including through inheritance.

class Animal {}

class Dog extends Animal {}

const dog1 = new Dog();

console.log(dog1 instanceof Dog);    // true
console.log(dog1 instanceof Animal); // true

A Dog instance is also an instance of Animal because Animal.prototype is in its prototype chain. Keep in mind that instanceof checks the prototype chain, not simply the name or shape of an object.

Checking Own and Inherited Properties

Sometimes you need to determine whether a property belongs directly to an object or is inherited from its prototype. Use Object.hasOwn() to check an object’s own properties. The in operator checks both own and inherited properties.

const parent = {
  category: "Vehicle"
};

const car = Object.create(parent);
car.brand = "Toyota";

console.log(Object.hasOwn(car, "brand"));    // true
console.log(Object.hasOwn(car, "category")); // false

console.log("brand" in car);    // true
console.log("category" in car); // true

The brand property belongs directly to car, while category is inherited from parent. Both properties are found by the in operator because it searches the prototype chain.

Static Inheritance in JavaScript Classes

When one class extends another, static methods and properties can also be inherited through the constructor’s own prototype chain. A static method is called on the class itself rather than on an instance.

class User {
  static getCategory() {
    return "General User";
  }
}

class Admin extends User {}

console.log(Admin.getCategory()); // "General User"
console.log(User.getCategory());  // "General User"

The Admin class can access the inherited static method from User. This is separate from instance-method inheritance, which uses the prototype chain of the class prototypes.

Types of Inheritance Patterns in JavaScript

JavaScript’s prototype-based model can be used to build several object relationships. The following terms describe common patterns rather than separate inheritance mechanisms built into the language.

1. Single inheritance: A child class extends one parent class, such as Dog extends Animal.

2. Multilevel inheritance: A class extends a class that already extends another class, forming a chain such as Animal, Mammal, and Dog.

class Animal {
  eat() {
    return "Eating";
  }
}

class Mammal extends Animal {
  walk() {
    return "Walking";
  }
}

class Dog extends Mammal {
  bark() {
    return "Barking";
  }
}

const dog = new Dog();

console.log(dog.eat());  // "Eating"
console.log(dog.walk()); // "Walking"
console.log(dog.bark()); // "Barking"

3. Hierarchical inheritance: Multiple child classes extend the same parent class. For example, both Dog and Cat can extend Animal.

4. Multiple behavior composition: JavaScript classes cannot extend multiple classes directly. When an object needs behavior from several sources, developers commonly use composition or mixin functions instead of trying to create multiple class inheritance.

Inheritance vs. Composition

Inheritance models an “is-a” relationship: a dog is an animal, for example. Composition models a “has-a” or “uses” relationship by combining smaller objects or functions to provide behavior. Composition can be easier to maintain when features do not fit naturally into a single parent-child hierarchy.

const canSwim = {
  swim() {
    return this.name + " is swimming.";
  }
};

const canWalk = {
  walk() {
    return this.name + " is walking.";
  }
};

const duck = Object.assign(
  { name: "Daisy" },
  canSwim,
  canWalk
);

console.log(duck.swim()); // "Daisy is swimming."
console.log(duck.walk()); // "Daisy is walking."

This example combines two behaviors into one object. It demonstrates composition: the object receives capabilities from separate sources without needing to extend multiple parent classes.

Categorized in:

Javascript, JS Basic,