Learnitweb

Inheritance and the prototype chain in JavaScript

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”.

prototypal inheritance in JavaScript

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.