The prototypal inheritance is how, in JavaScript, we share reusable properties and methods throughout objects. In this article, we'll see how it works.
Say we represented a user with the following object:
let user = {
name: 'John Snow',
getName() {
return this.name;
},
};
We want an admin user with the same shape and feature with an additional isAdmin property set to true. Here's how prototypal inheritance helps us achieve this without repeating ourselves:
let user = {
// ...
};
let admin = Object.create(user);
admin.isAdmin = true; // "isAdmin" is a new property set only in `admin`
admin.name = 'Daenerys Targaryen'; // "name" is an inherited property that is overwritten
console.log(admin.getName()); // Daenerys Targaryen ("getName" is also inherited from `user`)
By calling Object.create(user) we've made user the prototype of admin. It means that we have access to all the properties and methods of user through admin.
The Prototype Chain
When we call user.getName() and admin.geName(), two different things happen:
- We find
getNamedirectly inuser, but not inadmin. - For
admin, we implicitly go one step further in its prototype (user) to find the elusivegetName.
By going one step further to reach a prototype, we're walking through the prototype chain. This chain can expand if we introduce a root user, for example:
let user = {
//...
};
let admin = Object.create(user);
//...
let root = Object.create(admin);
root.isRoot = true;
root.name = 'Night King';
console.log(root.getName()); // Night King (inherited from `user`)
console.log(root.isAdmin); // true (inherited from `admin`)
console.log(root.isRoot); // true (proper to `root`)
Here, when we lookup root.getName, we walk through the prototype chain until the user prototype.
The ES6 class
An ES6 class and its instances also use a prototype behind the scene. When we define a constructor, it stores all of its properties and methods in the prototype property. Let's define a User class and some instances to see that in action:
class User {
constructor(name) {
this.name = name;
}
getName() {
return this.name;
}
}
const user1 = new User('Joe');
const user2 = new User('Kim');
console.log(User.prototype === user1.__proto__); // true
console.log(user1.__proto__ === user2.__proto__); // true
As you can see, the constructor and the instances all share the same prototype. We access the prototype using prototype in the constructor and __proto__ in the instances.
Wrap up
Sharing properties and methods between objects is important because it allows us to make our code reusable and well-organized. We achieve this in JavaScript through a concept called prototypal inheritance. A prototype is an object referenced by other objects seeking the same properties and methods. A prototype can also have a prototype which in turn has a prototype, and so on, forming the prototype chain. We walk through this chain when a property or method is not found directly in a given object.



Top comments (0)