1. Introduction
In this tutorial, we’ll discuss inheritance and the prototype chain in JavaScript. Prototype chain is one of the most confusing topic of JavaScript and is the most important concept to know in JavaScript as many cool features of JavaScript are based on prototypal inheritance.
In nature, you’ll see inheritance everywhere. Every man inherits from his parents. Our programs reflect the real world entities, their functions and communication with each other. JavaScript also provides support for inheritance like other programming languages.
JavaScript inheritance is prototype-based.
JavaScript does not provide the concept of
class
. In ES2015,class
was introduced but it is just a syntactical sugar, JavaScript is still prototype-based.
In JavaScript, objects have a special property [[Prototype]]
. This property is internal and hidden. This property holds a reference to another object or is null
(references no object). The object to which this property links is the prototype object. Object inherits properties from its prototype object. This is the “prototypal inheritance”.
2. __proto__
As mentioned earlier, the property [[Prototype]]
is internal and hidden. The property [[Prototype]]
can’t be accessed directly as it is internal and hidden. One way to access it is __proto__
. Please note that __proto__
is not the same as [[Prototype]]
. You can understand it as a historical a getter/setter for [[prototype]]
.
Since ECMAScript 2015, the [[Prototype]]
is accessed using the accessors Object.getPrototypeOf()
and Object.setPrototypeOf()
.
Let us understand this with help of an example.
var x = {}; console.log(__x__);
Output (in Google Chrome console)
constructor: ƒ Object()
hasOwnProperty: ƒ hasOwnProperty()
isPrototypeOf: ƒ isPrototypeOf()
propertyIsEnumerable: ƒ propertyIsEnumerable()
toLocaleString: ƒ toLocaleString()
..................
In the output, __proto__
refers to the Object.prototype
. Nearly all objects in JavaScript are instances of Object
.
If you go up the chain, i.e. use __proto__
once more, you’ll get null
.
x.__proto__.__proto__ // null
Every object has a private property [[prototype]] which refers to another object called as its prototype. This prototype itself has [[prototype]] property which links to its prototype. This chain can go on util an object is found with its prototype as null
. null
has no prototype and is the end of the prototype chain.
Similarly, whenever you try to access a property of an object, it is checked whether the object has the property. If not found then its prototype object is checked for the property and this search goes on till the property is found or the end of chain is reached.
3. Setting prototype using __proto__
To see how you can create one object as a prototype of another, let us see an example.
We’ll create two objects car
and vehicle
. We’ll then create a relation between these two objects.
var car = { wheels: 4, }; var vehicle = { drive: true, }; //create prototype of car car.__proto__ = vehicle; console.log(car.drive); // true. drive is inherited by car from vehicle
If you check __proto__
of both car
and vehicle
, you’ll observe the following output:
constructor: ƒ Object()
hasOwnProperty: ƒ hasOwnProperty()
isPrototypeOf: ƒ isPrototypeOf()
propertyIsEnumerable: ƒ propertyIsEnumerable()
toLocaleString: ƒ toLocaleString()
..................
But once you create a relationship between these two by using __proto__
, car.__proto__= vehicle;
the output (in Google Chrome console) for car.__proto__
is:
drive: true
[[Prototype]]: Object
If you want to check if the property is owned by the object or inherited from the prototype object, you can use hasOwnProperty()
method which all objects inherit from Object.prototype
.
car.hasOwnProperty('drive') // false
4. Object.getPrototypeOf() and Object.setPrototypeOf()
__proto__
acts as a getter and setter for [[prototype]]
property for historical reasons. The recommended way do it now is using Object.getPrototypeOf()
and Object.setPrototypeOf(
).
4.1 Object.getPrototypeOf()
The Object.getPrototypeOf()
method returns the prototype of the specified object. This method provides a way to access value of internal [[prototype]]
property. The syntax of this method is:
Object.getPrototypeOf(obj)
Here, obj
is the object whose prototype is to be retrieved. If there is no prototype available then null
is returned.
var x = {} console.log(Object.getPrototypeOf(x));
Output
constructor: ƒ Object()
hasOwnProperty: ƒ hasOwnProperty()
isPrototypeOf: ƒ isPrototypeOf()
propertyIsEnumerable: ƒ propertyIsEnumerable()
toLocaleString: ƒ toLocaleString()
toString: ƒ toString()
.....................
4.2 Object.setPrototypeOf()
The Object.setPrototypeOf()
method sets the prototype of the specified object. This method provides a way to set the value of internal [[property]]
property. The syntax of this method is:
Object.setPrototypeOf(obj, prototype)
The only supported values are another object or null
. No other value is supported. A TypeError
exception is thrown if you try to set [[prototype]]
property of a non-extensible object according to Object.isExtensible()
.
Using
Object.setPrototypeOf()
method is the correct way of setting[[prototype]]
property instead of the__proto__
.
var car = { wheels: 4, }; var vehicle = { drive: true, }; Object.setPrototypeOf(car, vehicle);
5. Inheriting “methods”
Methods in JavaScript are treated as any other property of the object. Methods are inherited and can also be added to objects like any other property.
In case of inheritance, this
points to the inheriting object not the prototype object.
//vehicle object var vehicle = { drive: true, wheels: 4, getWheels: function () { console.log(this.wheels); }, }; //car object var car = {}; //Set prototype of car Object.setPrototypeOf(car, vehicle); //set wheels of car car.wheels = 8; //'getWheels' is inherited from vehicle car.getWheels(); // Output is 8 as 'this' refers to the 'car'
6. Prototype of function
In JavaScript, functions can also have properties. All functions have a property named prototype
.
A function always have a default
prototype
property.
Try the following code in browser console.
function hello() { console.log("hello"); } hello.prototype;
Output
constructor: ƒ hello()
[[Prototype]]: Object
It is possible to add property to the prototype of the function as well.
// create the function 'hello' function hello() { console.log("hello world"); } // add a property 'message' to the prototype of function hello.prototype.message = 'new message'; console.log(hello.prototype);
Output (in Google Chrome console)
message: "new message"
constructor: ƒ hello()
[[Prototype]]: Object
6.1 Arrow functions do not have prototype prototype
Arrow functions do not have default prototype
property.
const hello = () => { console.log("hello world"); }; console.log(hello.prototype); //undefined
7. Prototype chain in objects
Following are different ways to create object and what will be the prototype chain in all these cases.
7.1 Prototype of object created with syntax constructs
var x = {};
Prototype chain: x —> Object.prototype —> null
7.2 Prototype of array
var arr = ['one', 'two', 'three'];
Prototype chain: arr —> Array.prototype —> Object.prototype —> null
7.3 Prototype of function
function func() { console.log('hello world'); }
Prototype chain: func —> Function.prototype —> Object.prototype —> null
7.4 Prototype of object created by constructor
function Car() { this.wheels = 4; } var c = new Car();
Prototype chain: c —> Car.prototype —> Object.prototype —> null
7.5 Prototype of object created using Object.create()
var car = {wheels: 4}; var mycar = Object.create(car);
Prototype chain: myCar —> car —> Object.prototype —> null
8. Conclusion
In this tutorial, we discussed inheritance and prototypal inheritance in JavaScript. We also discussed the prototypal chain for objects created by different ways.
We hope this tutorial was informative.