In our previous article, we looked at a few ways of creating objects. However, we did run into an interesting problem, namely that of unnecessary code reuse. Let us try to understand it better with an example.
let bird1 = {
type: 'sparrow',
canFly: true
}
let bird2 = {
type: 'eagle',
canFly: true
}
We have created 2 objects, bird1
and bird2
. We can see that while the value of the type property differs, the value of the canFly property remains the same. Rather than have the same property repeated in both objects, wouldn't it be better if we could have a single object called bird
, which has a property canFly set to true (if we assume that all birds can fly), and somehow let Javascript know that both the bird1
and bird2
object are going to be inheriting (or copying) the properties of that bird
object? In this way, we could have a single bird
object in which we could store all the properties that are common in birds and only need to include the unique properties in bird1
and bird2
. Something like this
let bird = {
canFly: true,
laysEggs: true,
hasFourLegs: false
}
let sparrow = {
color: 'blue'
}
let eagle = {
color: 'brown'
}
console.log(sparrow.canFly); // Returns undefined now but we ideally want a scenario where it returns true
Javascript allows us to implement the above concept with the special property of an object called [[Prototype]].
[[Prototype]]
In Javascript, objects have an internal property [[Prototype]], which is either another object or null. Although [[Protoype]] is the name given in the ECMAScript specification, for the purposes of this article, we shall be using the term 'prototype'.
The prototype of a Javascript object can be considered as its parent object or its super object. This means that when we try to access a property in an object, and it's missing, Javascript then tries to look for that property in the object's prototype and access it. This is referred to as 'prototypal inheritance'.
Although the prototype is an internal and hidden property of an object, there are other ways to access it. One of them is to use the '__proto__' keyword.
The __proto__ property
Let us see an example of using the __proto__ property for our previous example.
let bird = {
canFly: true;
laysEggs: true;
hasFourLegs: false
}
let sparrow = {
color: 'blue'
}
let eagle = {
color: 'brown'
}
sparrow.__proto__ = bird;
eage.__proto__ = bird;
console.log(sparrow.canFly); // Returns true
In the above code, you can see that we have set the prototype of sparrow
as bird
using the __proto__ property. Now, when we try to access the canFly property of sparrow
, Javascript first looks for it in sparrow
. When it doesn't find it there, Javascript then searches for it in its prototype (in this case, bird
) and finds it there. Hence, sparrow.canFly is evaluated to true. Similarly, since the prototype of eagle
is set to bird
too, eagle.canFly also works and evaluates to true.
In the above example, we can say that bird
is the prototype of sparrow
, or that that sparrow
'prototypically inherits' inherits from 'bird'. The properties of bird
, namely canFly, laysEggs and has4Legs, are called as inherited properties.
We can chain prototypes too.
let object1 = {
property1: 'exists'
}
let object2 = {
property2: 'exists'
__proto__: object1
}
let object3 = {
property3: 'exists'
__proto__: object2
}
console.log(object3.property1); // 'exists'
When we look for property1 in object3
, Javascript doesn't find it. It then looks for it in its prototype, which is object2
. It doesn't find property1 in object2
, and further looks for it in object2
's prototype (which is object1
). It then finds property1 in object1
and returns its value.
Now, you might be wondering, in the above example, what is the value of the prototype of object1
? Is it going to be undefined
, or is it an empty object? The answer is that it will be null
since the prototype is an internal property of every object in Javascript, which can either be another object or null.
In fact, there's a more elegant way of specifying an object's prototype while creating it itself. It's done via the Object.create
function.
Object.create
Calling the Object.create
function does 3 things:
- Create an empty object
- Set the prototype of the newly created object as the argument passed in the Object.create() function. This argument is mandatory and can only be either another object or null.
- Return the created object.
let object1 = {
property1: 'exists'
}
let object2 = Object.create(object1);
console.log(object2.property1); // 'exists'
Hence, as we have seen so far, we can make use of the hidden [[Prototype]] property in Javascript to implement the concept of inheritance and organize our code in a much more efficient and structured manner. In the next article of this series, we shall be discussing the new
keyword and how it functions under the hood to create objects in Javascript.
Top comments (2)
Hi there, great article and well explained.
Isn't
__proto__
obsolete now? I though thatObject.setPrototypeOf
andReflect.setPrototypeOf
were the successors for that API?Yes, __proto__ is considered to be obsolete now and instead
Object.setPrototypeOf
andObject.getPrototypeOf
are recommended for a number of reasons. The reasons are explained clearly in javascript.info/prototype-methods.