Object.create
In the previous lectures, we learned about constructor functions and ES6 classes, but there is actually a third way of implementing prototypal inheritance. And the third way is using the Object.create
method, which works in a pretty different way than constructor functions and classes.
With Object.create
, we still have the idea of prototypal inheritance. However, no prototype properties are involved, no constructor functions, and no new
keyword. Instead, we can use the Object.create
method to essentially set an object's prototype manually to any other object we want.
Hmmm, that sounds cool 😎. So if we can set the prototype to any object, let's create an object we want to use as the prototype of all the person objects we create. Essentially we will recreate the Person
class from the previous lectures.
Let's assume we have a PersonProto
object that will be used as the prototype of all the person objects we create.
const PersonProto = {
// ...
};
Inside this object, we will add exactly what we added before in the prototype property of the Person
class from the previous lectures.
const PersonProto = {
calculateAge() {
console.log(2022 - this.birthYear);
},
};
Now, all we need to do is to create a person object, with PersonProto
as the prototype.
const hart = Object.create(PersonProto);
This will now return a brand new object that is linked to the prototype that we passed into the Object.create
method. In this case, PersonProto
. So, hart
is right now an empty object, and it will be linked to the PersonProto
object, which will be its prototype.
Let's check that.
console.log(hart);
Let's add some properties to hart
.
/**
* This is not the best way to add properties to an object, but we will fix this later 😉
*/
hart.lastName = 'Hart';
hart.firstName = 'Louis';
hart.birthYear = 1991;
hart.profession = 'Developer';
With the properties added, we should now be able to calculate the age of hart
.
hart.calculateAge(); // 31
And it works! ⚡️. So, just like before, we implemented prototypal inheritance but in a completely different way. And just to ensure that we are on the same page and that we understand this difference, let me show you a diagram of what's really happening here.
Below is a diagram showing how it works with constructor functions, just as we have been doing up until this point.
So when we use the new
operator in constructor functions or classes, it automatically sets the prototype of the newly created object to the constructor's prototype property. And that's nothing new to us.
On the other hand, with Object.create
, we can set the prototype of objects manually to any object we want. And in this case, we manually set the prototype of hart
to PersonProto
.
And with this, the two objects are effectively linked through the __proto__
property just like before. So, now, looking up properties or methods in the prototype chain works just like it worked in function constructors or classes.
The big difference is that we didn't need any constructor function and no prototype
property to achieve the same result. And this method is more straightforward, and sometimes, I feel like it's easier to understand. However, I showed you this technique at the end because it's not used that often in practice. But it's still good to know about it.
Let's get back to our code and verify what we just learned.
console.log(hart.__proto__); // { calculateAge: [Function: calculateAge] }
This makes sense because we explicitly said that PersonProto
should be the prototype of hart
when we created it with Object.create
. Hence the following is also true
.
console.log(hart.__proto__ === PersonProto); // true
Now that we understand what's going on here, let's create another person object.
const james = Object.create(PersonProto);
But now, instead of adding the properties manually, let's create a method that will programmatically add the properties to the object.
const PersonProto = {
calculateAge() {
console.log(2022 - this.birthYear);
},
// You can use any name
build(firstName, lastName, birthYear, profession) {
this.firstName = firstName;
this.lastName = lastName;
this.birthYear = birthYear;
this.profession = profession;
},
};
We can now use this method to add properties to the object.
james.build('James', 'Hart', 1993, 'Developer');
If we now call the calculateAge
method, it should work just like before.
james.calculateAge(); // 29
To finish this lecture, note that the build
method above has nothing to do with the constructor functions that we saw in the previous lectures. And it is also completely different from the constructor
method that we saw in classes. It's just a manual way of initializing the properties of an object.