Mastering JavaScript: Unveiling the Power of the Prototype Property

A detailed explanation of how prototype chain works in JavaScript

Let's begin by first understanding why proto property is needed in JavaScript before deep diving into world of proto property.

Imagine you have naive game where we counting the number of time a button is clicked in a limited time and winner is with maximum number of clicks. So now to store the state of the game we will have at least two user instance which will keep track of the number of clicks.

const user1 = {}; //create an empty object
user1.name = "John"; //assign properties to that object
user1.numberOfClicks = 0;
user1.increment = function () {
  user1.numberOfClicks ++;
};

const user2 = {}; //create an empty object
user2.name = "Jane"; //assign properties to that object
user2.numberOfClicks = 0;
user2.increment = function () {
  user2.numberOfClicks ++;
};

So we should have something like above to store the state of game where we will store the numberOfClicks and a function increment . Well this looks good and game got popular and there are tons of users are playing the game and as the number of users increase our games starts crashing because of high memory consumption. Do you see the what is the problem here?

In the user objects we are storing the increment function in each user instance which is causing high memory consumption, also we are breaking our DRY principle here. So we can fix our problem if somehow we can store them on a single object and have JavaScript go look up the there.

Prototype Chain

So we can do this by Prototype chain, so when we call user1.increment rather than throwing an error it should somehow link user1 with our utility functions stored in an object. Let's look our first solution.

function newUserCreator(name, numberOfClicks){
    const newUser = Object.create(utilFunctons);
    newUser.name = name;
    newUser.numberOfClicks = numberOfClicks;
    return newUser;
}

const utilFunctons = {
    increment : function () {
          this.numberOfClicks++;
        }
}

const user1 = newUserCreator('John Doe', 6);
user1.increment();

Now if run our improved version of the game, we will see that the increment function is not actually present in the user1 object, but still somehow the increment functions is invoked the function runs with appropriate value, somehow the user1 objects has a hidden link to our utilFunctions object.

If we observe our user1 object has another hidden property __proto__ in latest specs of JavaScript this property is now referred as [[Prototype]] which we can see in the console as well. Let's understand how the __proto__ got the reference to our utilFunctions object.

The magic happens at here const newUser = Object.create(utilFunctons); the Object.create functions returns an empty object and creates a link or reference to the object we pass as the argument.

Let's see how the look up process will go:

  1. When we call the user1.increment() it will first look into the memory and will find the user1 object.

  2. Next, it will try to find the increment property in the user1 object and it will not find it as the property does not exist in user1 object.

  3. Next, it will go and look in the hidden property __proto__ of user1 object due to JavaScript prototypal nature and there it will find the increment function.

  4. The increment function is invoked.

And this is it, the prototypal nature of JavaScript. In JavaScript, the prototype chain is the mechanism by which objects inherit properties and methods from other objects. Each object has an internal link to another object called its prototype, creating a chain that ends with null. This allows property and method lookups to traverse up the chain until found.

The prototypal nature of JavaScript is the underlying concept on which new, class are build on and how inheritance works in JavaScript.