Search⌘ K
AI Features

JavaScript Prototypes

Explore JavaScript prototypes to understand how object inheritance works in OOP. Learn to access and modify prototypes using __proto__ and methods such as Object.getPrototypeOf and Object.setPrototypeOf. This lesson helps you grasp how prototypes connect objects and influence property lookup.

Background

We know about JavaScript objects and their mutation. We have assigned properties from one object to another. The question still remains. Is there a neater way of having all properties of another object?

Introduction to prototypes

All JavaScript objects have the property prototype. They go under the property name __proto__. The prototype of each object is assigned during creation and is itself an object. Look at the following code.

Node.js
let vehicle = { wheels : 4 }; // object assigned to variable named vehicle
let car = { seats : 5 }; // object assigned to variable named car
let driver = {} // empty object assigned to variable named driver
// Print all objects and __proto__ property for each variable
console.log(`vehicle:`, vehicle, vehicle.__proto__);
console.log(`car:`, car, car.__proto__);
console.log(`driver:`, driver, driver.__proto__);

In the code above, each object is assigned to the three variables: vehicle, car, and driver (lines 1 to 3). We also print the __proto__ property for each variable, which is otherwise hidden when printing an object including an empty object (driver). The __proto__ property for each object can be seen as an empty object because the properties of the prototype object are hidden. But it can be seen through another medium such as a browser console. The following layout shows the properties of the object assigned to the variable vehicle (line 1).

Shell
-{wheels: 4}
--wheels: 4
__proto__:
--constructor: f Object()
--hasOwnProperty: f hasOwnProperty()
--isPrototypeOf: f isPrototypeOf()
--propertyIsEnumerable: f propertyIsEnumerable()
--toLocaleString: f toLocaleString()
--toString: f toString()
--valueOf: f valueOf()
--__defineGetter__: f __defineGetter__()
--__defineSetter__: f __defineSetter__()
--__lookupGetter__: f __lookupGetter__()
--__lookupSetter__: f __lookupSetter__()
--get __proto__: f __proto__()
--set __proto__: f __proto__()

From the layout, we can see all the properties of the prototype object (__proto__). All these properties are essential for the classification of objects and implementing OOPs in JavaScript.

Prototypes usage

Now that we have seen prototypes in JavaScript, let’s manipulate them for our uses.

Node.js
let vehicle = { wheels : 4 }; // object assigned to variable named vehicle
let car = {
seats : 5,
__proto__ : vehicle // __proto__ property assigned to vehicle
}; // object assigned to variable named car
// Print all objects and __proto__ property for each variable
console.log(`vehicle:`, vehicle, vehicle.__proto__);
console.log(`car:`, car, car.__proto__);
console.log(`vehicle seat:`,vehicle.seats);
console.log(`car wheels:`, car.wheels);

The above code is like our previous example, except for the object assigned to variable car, we now assign the prototype property __proto__ to variable vehicle (line 4). Printing the prototype object of the object assigned to the car variable shows that it is the object assigned to the variable vehicle (line 9).

When we print the property seats (property of car object) for object assigned to variable vehicle, we get undefined value. This shows that the value was not accessible (line 10). However, when we print the property wheels (property of vehicle object) for object assigned to variable car, we get 4 showing that the value was accessible (line 11). This is because the __proto__ property of the car object was assigned to the object assigned to vehicle (line 4). The following illustration visualizes the concept.

This illustration shows how the objects connect to one another after assigning the __proto__ property of the object assigned to the variable car to vehicle. Assigning vehicle to car object’s __proto__ property allows car object to access properties of the object vehicle.

The __proto__ property allows accessing properties of another object, if it was assigned to it. In our case, it allows access to the wheels property of vehicle object.

What would happen if the car object had its own property wheels?

Node.js
let vehicle = { wheels : 4 }; // object assigned to variable named vehicle
let car = {
seats : 5,
__proto__ : vehicle, // __proto__ property assigned to vehicle
wheels : 6,
}; // object assigned to variable named car
// Print all objects and __proto__ property for each variable
console.log(`vehicle:`, vehicle, vehicle.__proto__);
console.log(`car:`, car, car.__proto__);
console.log(`car wheels:`, car.wheels);

With the code above, we can see that despite __proto__ being assigned to vehicle (line 4), the car.wheels (line 11) value is equivalent to 6 because the car object has its own wheels property (line 5). The __proto__ property is only checked if the immediate object has no property with the respective name.

The __proto__ link is traversed after confirming that a target property doesn’t already exist.

NOTE: The prototype object itself has a __proto__ property assigned to a null value. The __proto__ property should be assigned to either null, a prototype object, or another object. It is depreciated to assign it to any other value.

Accessing and changing prototypes

So, we learned about the __proto__ property and have assigned and accessed it directly. These assignments or retrieval of prototype objects of an object implicitly call the following two methods:

  • Object.getPrototypeOf(obj): Retrieves the value of __proto__ property of the obj object
  • Object.setPrototypeOf(obj, proto): Assigns the value of proto to __proto__ property of the obj object

Let’s rewrite our code from the previous example using the respective methods.

Node.js
let vehicle = { wheels : 4 }; // object assigned to variable named vehicle
let car = { seats : 5 }; // object assigned to variable named car
Object.setPrototypeOf(car, vehicle);
// Print all objects and __proto__ property for each variable
console.log(`vehicle:`, vehicle, Object.getPrototypeOf(vehicle));
console.log(`car:`, car, Object.getPrototypeOf(car));
console.log(`car wheels:`, car.wheels);

In the code above, using the Object.getPrototypeOf and Object.setPrototypeOf methods give the same results.