Academic Block

Prototypes & Inheritance Get Certified

Almost everything in JavaScript is an object, and every object has a hidden link to another object called its prototype. Understanding the prototype chain is the key to understanding inheritance in JavaScript.

What Is a Prototype?

When you try to access a property that does not exist on an object, JavaScript looks for it on the object’s prototype, then on that prototype’s prototype, and so on — this is the prototype chain.

What this code does: Creates a simple object and shows that it inherits methods from Object.prototype.

const person = {
    name: "Alice"
};

console.log(person.name);
console.log(person.toString());          // inherited from Object.prototype
console.log(Object.getPrototypeOf(person) === Object.prototype);

Constructor Functions and the Prototype

When you create objects with a constructor function and the new keyword, methods should be placed on the constructor’s prototype so they are shared by all instances.

What this code does: Defines a constructor and adds a method to its prototype. Every instance shares the same method.

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

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

const dog = new Animal("Rex");
const cat = new Animal("Whiskers");

console.log(dog.speak());
console.log(cat.speak());
console.log(dog.speak === cat.speak);   // true – same shared function

Object.create()

Object.create() creates a new object with the specified object as its prototype, without needing a constructor function at all.

What this code does: Uses Object.create() to make a new object whose prototype is an existing object, then calls an inherited method.

const animal = {
    speak: function () {
        return this.name + " makes a sound.";
    }
};

const dog = Object.create(animal);
dog.name = "Rex";

console.log(dog.speak());
console.log(Object.getPrototypeOf(dog) === animal);

Inheritance with Constructor Functions

Before class syntax, one constructor function could inherit from another by linking their prototypes together with Object.create().

What this code does: Implements inheritance between constructor functions by linking prototypes and calling the parent constructor.

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

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

function Dog(name) {
    Animal.call(this, name);
}

Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

Dog.prototype.bark = function () {
    return this.name + " barks.";
};

const rex = new Dog("Rex");

console.log(rex.speak());
console.log(rex.bark());

ES6 Classes

The class keyword is syntactic sugar over the same prototype system. It provides a cleaner syntax for creating constructor functions and prototype methods.

What this code does: Introduces modern class syntax. Under the hood the class still creates a constructor and places methods on its prototype.

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

    speak() {
        return this.name + " makes a sound.";
    }
}

const cat = new Animal("Whiskers");

console.log(cat.speak());
console.log(typeof Animal);   // "function"

Inheritance with extends and super

The extends keyword creates inheritance between classes, and super() calls the parent class’s constructor.

What this code does: Shows a subclass that extends a parent class, calls super(), and inherits the parent’s methods.

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

    speak() {
        return this.name + " makes a sound.";
    }
}

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

    bark() {
        return this.name + " barks.";
    }
}

const rex = new Dog("Rex", "Labrador");

console.log(rex.speak());
console.log(rex.bark());
console.log(rex.breed);

Overriding Methods with super

A subclass can override a parent method and still call the original version using super.methodName().

What this code does: Overrides a method in the child class and uses super.speak() to invoke the parent version.

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

class Dog extends Animal {
    speak() {
        const base = super.speak();
        return base + ", but specifically a bark";
    }
}

const rex = new Dog();
console.log(rex.speak());

instanceof and the Prototype Chain

The instanceof operator checks whether an object’s prototype chain includes a given constructor’s prototype.

What this code does: Uses instanceof to check an object’s place in the prototype chain.

class Animal {}
class Dog extends Animal {}

const rex = new Dog();

console.log(rex instanceof Dog);     // true
console.log(rex instanceof Animal);  // true
console.log(rex instanceof Object);  // true

Own Properties vs. Inherited Properties

hasOwnProperty() checks whether a property exists directly on the object itself, rather than somewhere up the prototype chain.

What this code does: Distinguishes own properties from inherited methods using hasOwnProperty().

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

    speak() {
        return this.name + " makes a sound.";
    }
}

const rex = new Animal("Rex");

console.log(rex.hasOwnProperty("name"));   // true
console.log(rex.hasOwnProperty("speak"));  // false

Constructor Functions vs. Classes vs. Object.create()

Feature Constructor Function ES6 Class Object.create()
Requires new keyword? Yes Yes No
Inheritance syntax Object.create(Parent.prototype) extends / super Pass parent object directly
Under the hood Prototypes Prototypes Prototypes
Common in modern code? Rarely (legacy) Yes Sometimes

Common Mistakes with Prototypes

  • Defining methods inside the constructor instead of on the prototype, wasting memory.
  • Forgetting to reset Dog.prototype.constructor after reassigning the prototype.
  • Forgetting to call super() before using this inside a subclass constructor.
  • Modifying built-in prototypes such as Array.prototype.
  • Confusing __proto__ with the constructor’s prototype property.

Best Practices for Prototypes & Inheritance

  • Prefer class syntax for new code — it is clearer and less error-prone.
  • Put shared methods on the prototype (or inside a class body), not inside the constructor.
  • Always call super() first in a subclass constructor before accessing this.
  • Avoid modifying the prototypes of built-in objects like Array or Object.
  • Prefer composition over deep inheritance chains when objects do not truly share an “is-a” relationship.

Quick Summary

  • Every object has a prototype it can fall back on for properties and methods it does not have itself.
  • Property lookups travel up the prototype chain until they are found or the chain ends.
  • Methods placed on a constructor’s prototype are shared by every instance.
  • Object.create() creates a new object with a specific prototype directly.
  • class, extends, and super are modern syntax built on top of the same prototype system.
  • instanceof checks whether a constructor’s prototype appears in an object’s prototype chain.
  • hasOwnProperty() distinguishes an object’s own properties from ones it inherited.

Try It Yourself

Modify the classes below. Try adding a new method, overriding speak() in Cat, or checking instanceof against different classes.

What this code does: A complete practice example combining class inheritance, method overriding with super, instanceof, and hasOwnProperty().

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

    speak() {
        return this.name + " makes a sound.";
    }
}

class Cat extends Animal {
    speak() {
        return super.speak() + " Specifically, a meow.";
    }
}

const whiskers = new Cat("Whiskers");

console.log(whiskers.speak());
console.log(whiskers instanceof Animal);
console.log(whiskers.hasOwnProperty("name"));
console.log(whiskers.hasOwnProperty("speak"));
Tip: Under the hood, class syntax, constructor functions, and Object.create() all rely on the same prototype chain — learning to read that chain makes it much easier to understand how inheritance really works in JavaScript.

Understanding prototypes and inheritance explains what happens behind the scenes of every class you write, and prepares you for more advanced topics like mixins, the this keyword, and closures.

🧪 Test Your JavaScript Code

Edit the code on the left and click “Run Code” to see the console output on the right.

📝 JavaScript Code
👁 Console Output

Click “Run Code” to see the output here.

Ctrl + Enter to run code Esc to close