Understanding Prototypes in JavaScript
I am sure you must have been through the basics of JavaScript.By now, you might know that arrays, functions, objects, dates are all objects.JavaScript is a prototype-based language which means that inheritance works by something called prototypes. Each and every object in JS has a prototype property.
OK, now enough if the fuss about prototypes 😅 , let’s understand it.
The usual function constructor approach
As you might be aware of the function constructor approach in JS to create objects so we are going to create a function constructor as such:
var Person = function(name,yearOfBirth,job) {
this.name = name;
this.yearOfBirth = yearOfBirth;
this.job = job;
this.calcAge = function(){
console.log(2020 - this.yearOfBirth)
}
}
Now, we’ll create two objects Adam and Eve using the Person constructor function:
var Adam = new Person('Adam', 1998, 'teacher');
var Eve = new Person('Eve', 1993, 'painter');
The problem with the above approach is that upon executing the code JS will create two copies each of the properties and methods for the two objects.It isn’t efficient to have two instances of the function calcAge() as storing separate instances of functions for each object ends up wasting memory.Now that’s where prototypes come in.
Prototypes
Every object in JS has a prototype property.This prototype property is an object known as prototype object.It’s the prototype property which makes inheritance easier in JS.
Considering the same example if the object Adam wants to inherit a method or a property from the Person object we have to add that method or property to the Person’s prototype property.What is really important to note here is that the Person’s prototype is not the prototype of the Person itself but of all instances that are created through the Person blueprint.So, in this example the Person’s prototype property is the prototype of Adam.
So,the modified Person constructor is as:
var Person = function(name,yearOfBirth,job) {
this.name = name;
this.yearOfBirth = yearOfBirth;
this.job = job;
}
Person.prototype.calcAge = function(){
console.log(2020 - this.yearOfBirth)
}
So now when the calcAge() method is called on both the Adam and Eve objects:
Adam.calcAge(); //22
Eve.calcAge(); //27
Now you can observe that while the method calcAge() is not directly in the Person object but we can still access it because it is in the prototype property of the Person function constructor.
Properties to an object can also be added through the function constructor:
Person.prototype.lastName = 'Wallace';
which can be accessed as:
Adam.lastName; // Wallace
Eve.lastName; // Wallace
Let’s open up the Adam,Eve and Person.prototype in Chrome console:
Notice how the Adam’s,Eve’s __proto__ property and Person.prototype looks the same.Let’s check if they are infact the same or not:
Person.prototype === Adam.__proto__ //true
Adam.__proto__ === Eve.__proto__ //true
Above statement proves that the Adam’s and Eve’s __proto__ properties point to Person constructor function’s prototype object.
Prototype Chaining
In JS, each and every object that we ever create is an instance of the Object constructor which has a bunch of methods in its prototype property.
Sounds confusing right? Let’s consider our original example again:
What we already know is that Adam is the instance of Person constructor.But that’s not all because the Person object itself is the instance of an even bigger constructor which is the Object object.Sounds craze right? but that’s how it works.
Each and every object that we create is an instance of the Object constructor that has a bunch of methods in its prototype property.
As you might be guessing right now the Person object can inherit those methods and call them plus the Adam object also inherits these methods and can also use them.Let’s validate this in the console by inspecting the Adam object
If we look into the prototype of Adam we notice that there is also a __proto__ object attached to it,this is the prototype property of the Object constructor which contains bunch of methods associated to the prototype property of the Object function constructor.That explains the whole prototype chain.
As we discussed we have access to those methods of Object constructor and can be used on the Adam object.
Adam.hasOwnProperty('job') // true
Adam.hasOwnProperty('lastName') // false//because hasOwnProperty() looks for only own properties and we inherited lastName from the prototype,it's not really Adam's own property
Adam instanceof Person // true
So, that’s all for our example but before concluding let’s take a look at arrays in the context of prototype chaining
We’ll define an array and take a look at it in the console
Upon expanding it we notice how the length property which you might remember is used to calculate length of the array and it’s always stored right here in the array instance and that’s why we can use it so easily.It get’s even better as we see the prototype of this array __proto__ which of course is the Array function constructor that’s built right into JS and allows us to build arrays and here in it’s prototype property we can see all of the methods we can use with the arrays like pop(), push(), slice() etc.So, now you exactly know how you are able to call these methods on arrays, because of the prototype chain
That’s how useful inheritance really is!
Happy Coding!! 😇