Understanding Prototype and Prototype Chaining in JavaScript
Let's understand Prototype by taking a goal to achieve in mind. Suppose you want to build something like React, ViewerJS, String Library, or a classic Array Library or you want to do some DOM Manipulation. I'm talking about big steps here, like building your own library, but that will take an insane amount of code. But let's understand the ground required for it.
Let's say you take a string variable myName and inject it as "Saloni", so its length is 6.
let myName = "Saloni"; // length is 6
Now you give 4 spaces after your name, you can verify it with the length property. It'll now return 10 (6+4).
let myName = "Saloni ";
myName.length; // Returns 10
But here you want additional functionality. There should be a property something like "trueLength", that will give you the actual length of your string and neglect the spaces.
So, if someone uses your framework and wants to get the actual length of a string, they can simply use the "trueLength" property.
You'll be able to make it by the end of this article, but first, let's understand some basics with examples.
let morning = ["Meditate", "Exercise"];
let night = ["Journal", "Read", "Detox"];
morning; // This will return (2) ['Meditate', 'Exercise']
As shown above, two arrays named morning
and night
are declared and if you access one of them, e.g., morning
and expand it, you'll get that there are two values in the array, the 0th value being "Meditate", and the 1st value "Exercise", and there is a property length that is giving you the length of the array.
But there is one more property [[Prototype]]: Array(0), which is not defined by you. If you will open or expand it, there are a lot of properties that you'll find very much familiar, like indexOf, map, push, pop, or unshift which are very common and popular.
Where do these extra properties come from?
So basically, these properties are coming from Prototype. What's happening here is, Array is inheriting some properties from the Object itself and these properties are being injected into the Prototype.
What is a Prototype in JavaScript?
JavaScript is a prototype-based language. If you know javascript objects, you must have read that "Nearly everything in javascript is an object". Please read my article on JavaScript Object in order to have a solid idea about what are javascript objects.
Every object in JavaScript has a built-in property, known as its prototype, simply said all JavaScript objects inherit properties and methods from a prototype, such as
Date objects inherit from Date.prototype
Array objects inherit from Array.prototype
Morning objects inherit from Morning.prototype.
In the above screenshot, you can see at the end, there is a [[Prototype]]: Object.
The Object.prototype is on the top of the prototype inheritance chain. Date objects, Array objects, and Morning objects inherit from Object.prototype.
Prototypes are the mechanism by which JavaScript objects inherit features from one another.
And in order to achieve our goal to have a property like the true length of a string, you will have to learn, how to inject your own explicitly created properties in the Prototype.
Using the prototype Property
The JavaScript prototype property allows you to add new properties and new methods to object constructors.
So, you can use String.prototype
in order to add a new method to the object constructor. And to get the actual length of your string variable, you can use a string method known as trim(), which removes whitespace from both sides of a string without changing the original string.
String.prototype.trueLength = function() {
console.log(`true length is: ${this.trim().length}`);
}
let myName = "Saloni ";
myName.length; // Returns 10
myName.trueLength(); // Returns true length is: 6
Prototype Chaining
The prototype is itself an object, so the prototype will have its own prototype, making what's called a prototype chain. The chain ends when we reach a prototype that has null for its own prototype.
Javascript has an interesting inheritance model, which happens to be completely different from most OOP languages. While it is object-oriented, an object doesn’t have a type or a class that it gets its methods from, it has a prototype. It is important to understand the differences between these two, as they are not equivalent, and lead to much confusion down the line.
In order to understand that let’s do a small exercise. Let’s create a simple object and then alert the object.
const life = {};
alert('' + life); // Output => [object Object]
So, how do we get [object Object] as an output? life is an empty object and it doesn't have any properties which hold such data or function which can generate something like that. Let’s deep dive a bit further to understand what happened behind the scenes.
If you'll console out the life object, you'll find nothing but just an empty object.
const life = {};
console.log(life); // This will log {}
But if you expand the life object you'll see proto property in it. This is pointing to some function’s prototype object. Let’s console log that.
const life = {};
console.log(life.__proto__);
You'll see lots of functions on it. There is also toString() function on it. So the output you saw from alert has been generated from this method. Let’s confirm that by using it.
When you're alerting that life object, the JavaScript engine has called this toString
method in some object’s prototype object’s function in order to generate this output. But the question is whose prototype object is this?
In JavaScript, there are set of built-in Objects such as Object, Array, Date, Function etc and each of those objects will have respective prototype objects attached to it. In the above case, it was the Object construction function which invoked to create the life instance. That is because const life = {} is the same as const life = new Object() where Object is the built-in constructor function. And that function has Object.prototype, which has all the methods we saw earlier. If what we found out true then you can use the identity operator (===) to evaluate the facts that you are familiar with.
If Object constructor function was invoked when creating the life instance then Object.prototype and life.proto should point to the same prototype object.
Those were pointing to the same prototype object. And also following will also be obviously true if that prototype object belongs to the Object constructor function Object.
If an object tries to access the same property that is in the constructor function and the prototype object, the object takes the property from the constructor function.
function Person() {
this.name = 'Saloni'
}
// adding property
Person.prototype.name = 'Hitesh';
Person.prototype.age = 35
const person1 = new Person();
console.log(person1.name); // Saloni
console.log(person1.age); // 35
In the above example, a property name is declared in the constructor function and also in the prototype property of the constructor function.
When the program executes, person1.name looks in the constructor function to see if there is a property named name. Since the constructor function has the name property with value 'Saloni', the object takes value from that property.
When the program executes, person1.age looks in the constructor function to see if there is a property named age. Since the constructor function doesn't have age property, the program looks into the prototype object of the constructor function and the object inherits property from the prototype object
function Person () {
this.name = 'Saloni'
}
// adding a prototype
Person.prototype.rank = 10;
// creating object
const person = new Person();
// accessing prototype property
console.log(person.__proto__); // { rank: 10 }
In the above example, a person object is used to access the prototype property using proto
. However, proto
has been deprecated and you should avoid using it.
Hope you learned something.
Learn More about Prototype from MDN Docs