Sitemap
Alpha Tran

Alpha Tran

Full Stack Developer specializing mostly in JS and TS, but also have somewhat knowledge of Elixir, Python and C#

How inheritance works in JavaScript (Without the Magic)

4 min readFeb 17, 2026
Press enter or click to view image in full size

JavaScript ES6 has come with some syntactic sugar features, notably classes, which make the language more “object-oriented” and reduce the verbosity of the code we have to write to some extent. However, classes have somewhat abstracted the underlying behavior of prototypes and inheritance, which differs from OO languages like C# or Java.

Therefore, to become a truly competent developer, we can’t just use those features without fully grasping their inner workings. In this article, we’ll explore how inheritance actually works in JavaScript via some simple examples.

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

Animal.prototype.eat = function() {
console.log("Eating...");
}

function Bird(name, age, canFly = true) {
Animal.call(this, name, age);
this.canFly = canFly;
}

Bird.prototype = Object.create(Animal.prototype); // inherit the prototype chain as well

Bird.prototype.fly = function() {
if (this.canFly)
console.log("Flying...");
else
console.log("Cannot fly");
}

Bird.prototype.constructor = Bird; // override the constructor from Animal's to Bird's

const bird = new Bird("Cuckoo", 3, true);

In the snippet above:

  • Instances created by Animal function has two properties name and age, and has a method eat defined inside its prototype object.
  • Bird instance receives Animal's instance properties via constructor chaining, and inherit its prototype methods through the prototype chain.

There are some caveats you need to bear in mind:

We need to define additional methods on Bird.prototype after assigning it to Animal.prototype

We don’t directly define the method eat inside the Animal function and declare it on the prototype instead. This has some benefits:

  • Only one eat method will be reused in memory since objects instantiated from a function that inherits from its parent will use the same eat function in the prototype chain. This leads to better performance, especially with many objects.
  • Better leverage of JS’s prototype inheritance patterns (as we’ll see later).

In JS, inheritance works via what’s known as the prototype chain, which we’ll explore in the following section.

Prototype chain

Let’s look at how the code example above works under the hood.

If you try logging the bird object in the browser, you’ll see this:

The prototype chain of the bird object

In the image above, along with the attributes we’ve defined, there’s the [[Prototype]], which contains the properties and methods that the object inherits from its ancestors and can access inside the prototype chain.

Get Alpha Tran’s stories in your inbox

Join Medium for free to get updates from this writer.

The prototype chain of this bird object is:

     bird
↓
Bird.prototype
↓
Animal.prototype
↓
...
↓
Object.prototype
↓
null

When a property is accessed or a method is invoked, under the hood:

  • The engine attempts to look up in the chain above by following the internal [[Prototype]] reference (which we can inspect using Object.getPrototypeOf).
  • When it reaches null, the lookup returns undefined. Attempting to call it as a function throws an error like bird.x is not a function.

What about bird.toString()? Likewise, the method toString actually exists in the prototype of Object, which returns [object Object].

What if we override the toString method on Bird prototype?

Bird.prototype.toString = function() {
return this.name + " is " + this.age + " years old";
}

In this case, the first toString method found in the chain is in Bird.prototype. Therefore, it should return “Cuckoo is 3 years old”.

If you print the bird object again, you’ll see the method toString in Bird.prototype:

The prototype chain of the bird object after adding toString method

This is how JS enables polymorphism under the hood!

In a nutshell, inheritance in JavaScript is delegation, not copying. Objects delegate property lookup to other objects via the prototype chain.

Once again, to end this article, classes are just syntactic sugar for what we’ve done manually.

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

eat() {
console.log("Eating...");
}
}

class Bird extends Animal {
constructor(name, age, canFly = true) {
super(name, age);
this.canFly = canFly;
}

fly() {
console.log("Flying...");
}

toString() {
return this.name + " is " + this.age + " years old";
}
}

If you’ve made it this far, congratulations! Understanding how inheritance works and prototyping chains in JavaScript isn’t an easy thing for beginners.

Thank you for reading my article! Happy coding :)

--

--

Alpha Tran
Alpha Tran

Written by Alpha Tran

Full Stack Developer specializing mostly in JS and TS, but also have somewhat knowledge of Elixir, Python and C#