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:
When we call the
user1.increment()
it will first look into the memory and will find theuser1
object.Next, it will try to find the
increment
property in theuser1
object and it will not find it as the property does not exist inuser1
object.Next, it will go and look in the hidden property
__proto__
ofuser1
object due to JavaScript prototypal nature and there it will find the increment function.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.