JavaScript Prototype Chain: Short And Simple Guide

JavaScript Prototype Chain: Short And Simple Guide
Photo by Aida L / Unsplash

Before I begin, let me give a warning. This article is for those who want to understand how the prototype chain in JavaScript works.

It's going to be tough for some of you and it may seem not very clear on first read. You may have to re-read this a few times to understand it.

But it's gonna be worth it. The question about the prototype chain is asked pretty often in technical interviews.

You probably already know that arrays and functions are just objects in JavaScript. This is crucial to understanding the prototype chain.JavaScript uses something called prototypal inheritance. You see these arrows here:

They're prototypal inheritance. And what does that mean?

Inheritance is basically an object getting access to the properties and methods of another object.

So, everything is an object in JavaScript. Array gets access to the properties and methods of the object. Same with functions. Through this chain we call the prototype chain, functions get access to the methods and properties of objects.

Now let's take a look at this:

First, we have an empty array called "food".

Next, we access this weird property called "__proto__". It is written with 2 underscores before and after the word proto. We can see in the console, this weird property is an array of functions:

  • constructor,
  • at,
  • concat,
  • copyWithin,
  • fill,
  • push, etc.

Those are all methods that the array type of data gets by default.

So what we did here with this is getting up in the prototype chain. And getting into the Array prototype (notice the image and capital A). Since the Array is the "parent" of our "food" array we get all these methods inherited from.

Now, onto the next line, where we have this weird "__proto__" syntax 2 times. You might have guessed it right, with that, we go one more level up in the prototype chain. So we access the prototype of the "food" grandparent. And that's Object.

This is the object that everything in JavaScript gets created from. Including functions and arrays.

For example, we have the toString method over here. This means that anything that is a descendant of an Object will get the toString method. So that means that a "food" array has the toString method on it.

You see I get the same result for a function type:

And this is what prototype inheritance is.

As a matter of fact, prototype inheritance is actually quite unique. It's not that common in other popular languages like C# or Java. They use something called classical inheritance while JavaScript uses prototype inheritance.

Even though in JavaScript we do have the class keyword, there are actually no classes in JavaScript. We only have prototype inheritance.

So when you write a class keyword, you are using a function in the background. This is what we call syntactic sugar.

You might ask yourself, where is the end of the prototype chain?

Well, it's easy to find out:

Here we have our "food" object and we go 2 levels up. So the first level is our parent Object type, but if you go higher than that, you get null.

In JavaScript, undefined is often used to indicate a variable or object property has not been initialized or assigned a value. Or when a function doesn't return any value.

Null means there's absolutely nothing there. It represents the intentional absence of any object value.

You probably heard about linked lists, right? You can manifest the prototype chain in your head as a simple linked list:

All nodes are connected with their prototype chain links. The null is just the last node in the linked list of the prototype chain.

Prototype chain in the code

Let's take a look at this code snippet:

const human = {
    name: "Joe",
    canTalk: true,
    getIntelligence(){
        return 10;
    },
    talk(){
        if(this.canTalk){
            console.log(`Hi, my name is ${this.name}!`);
        }
    }
}

const chimpanzee = {
    name: "Mowgli",
    getIntelligence(){
        return 5;
    }
}

Nothing special there, we have 2 simple objects:

  1. human Joe. He can talk and his intelligence is 10 points. When he talks he says: "Hi, my name is Joe!"
  2. chimpanzee Mowgli. He can't talk and his intelligence is just 5 points.

Now, let's say I want to make Mogwli talk. How can I do that?

Well, I can use a trick with .bind method and "borrow" talk function from a human. Let's try this:

const talkingChimpanzee = human.talk.bind(chimpanzee);

console.log(talkingChimpanzee()); 
// prints undefined

Unfortunately, the chimpanzee doesn't have the canTalk property set to true. So even though we borrow the method because we don't have the talking ability.

So what can we do here?

We could add the property, but you can see how it might get more and more complicated here, right? What if we had a big object and we wanted to borrow more than one property/method?

And this is where prototype inheritance comes in.

We can create a prototype chain that will inherit all these properties and methods from a human.

chimpanzee.__proto__ = human;

chimpanzee.talk();                         // prints "Hi, my name is Mowgli!"
console.log(chimpanzee.canTalk);           // prints true
console.log(chimpanzee.getIntelligence()); // prints 5

We can see above is exactly what we needed.

The chimpanzee's intelligence is still 5 because it's defined as a chimpanzee object. So we are able to inherit through this prototype chain all the properties and methods of a human.

Then we override anything that we've already declared in our own object. In this case, it's property name and method talk.

hasOwnProperty method

Let me show you one more interesting thing:

console.log("Has name property: ", chimpanzee.hasOwnProperty('name'));
console.log("Has canTalk property: ", chimpanzee.hasOwnProperty('canTalk'));

// Has name property: true
// Has canTalk property: false

First, how is this even working?

We know that a chimpanzee doesn't have any method called hasOwnProperty. A human also doesn't have that function.

Well, a human has also a "parent" from which it inherits all methods and properties. That parent is Object and I mentioned that at the start. The Object is a parent of all parents, the final parent.

So in the background, JavaScript is first trying to find the hasOwnProperty function in a chimpanzee. But, it can't find it.

Then it goes up in the prototype chain and starts to search in a human. It can't find it.

Then, it goes one more level up in the prototype chain, in the Object, and there we have that method:

Now we know why chimpanzee has that hasOwnProperty method.

But what is the hasOwnProperty method doing?

Well, it returns a boolean indicating whether this object has the specified property as its own property. So not inherited property, but its own.

That's why in the last code snippet, the hasOwnProperty method displays true for name and false for canTalk.

The chimpanzee has its own name property defined in the object. So, canTalk property is not its own. It's inherited from a human and that's why it displays false.

Warning, warning, warning!

You might be asking yourself, how come I haven't seen this "__proto__" thing before?

It seems pretty useful. Why don't we see it in the projects?

What I've shown you, you shouldn't do in any project. And I mean this:

chimpanzee.__proto__ = human;

You shouldn't use it. Actually, you should NEVER use it!

It's bad for performance. There are different ways that we can use when it comes to prototype or inheritance.

So we never want to assign the prototype chain and create that chain ourselves. It's going to, mess up our JavaScript compiler pretty badly.

But I wanted to show you how it works.

What to use instead of "__proto__"?

Let's see how we can create our own prototypes and what is a safe way to do this.

Let's check this code:

const human = {
  canTalk: true
}

const chimpanzee = Object.create(human);
chimpanzee.jumpOnTree = true;

console.log(chimpanzee);
// prints {jumpOnTree: true}

console.log(chimpanzee.canTalk);
// prints true

console.log(human.isPrototypeOf(chimpanzee));
// prints true

So we have here a human that can talk. And then we have chimpanzee that inherits all from human. We do that with the Object.create method.

Now, there are many ways of doing this, and this is one of the ways that we can inherit from a human.

If we print a chimpanzee object all we can see is jumpOnTree property. But if we explicitly access the that property that is inherited from a human. We see it exists and is set to true.

Last thing, we check if a human is a prototype of a chimpanzee with the isPrototypeOf method. And we get true because we've created using Object.create a prototype chain up to a human.

Now you know how to do this without using that evil "__proto__" thing.

As a matter of fact, they named it this way (with double underscores) so that nobody messes with the prototype chain.

Why is the Prototype chain useful?

The fact that objects can share prototypes means that you can have objects with properties that are pointing to the same place in memory. Thus being more efficient.

Imagine if we had a huge number of chimpanzees, right? And we copied all this functionality of the human onto the chimpanzee into different places in memory.

That could get overwhelming soon and blow up your code.

Instead of copying all this functionality into different places in memory, with prototype inheritance, we have it in one place.

Anything that inherits from a human will use this one instance of this method. Because the JavaScript engine is going to look up the prototype chain.