Inheriting Object Prototypes
Open the project folder
In our starter files, open the index.html page from this lessons folder:
07.Objects-In-More-Depth > 04.Inheriting-object-prototypes
Limitations for our current example
We have discovered there is a parent JavaScript Object
which has a prototype. And how when we create new objects, they automatically inherit certain things through the prototype chain.
But what if we wanted to also allow our own objects to be inherited from too?
In the starter files we have a User
object, and we may begin to realise that this object has some limitations for our use:
function User(first, last, occupation, lives) {
this.firstName = first;
this.lastName = last;
this.occupation = occupation;
this.lives = lives;
}
const chris = new User('Chris', 'Dixon', 'Dev', 'UK');
const homer = new User(
'Homer',
'Simpson',
'Safety Inspector',
'Springfield'
);
console.log(homer);
Considering the above example, one could be a real person, and one a fictional charater from a TV series. Real and fictional people may need different properties.
We may for example want to add the name of the show which the fictional character was from, but this would not apply to the real person, and this now makes it difficult to expand this object much further.
What we can do is have the User
object as a base, including only the properties which both the real and the fictional users share.
Then we can create two more objects with the specific properties we need. Both will inherit from the original User
, so we still have access to things like the name and occupation.
The Object.create()
method
To do this, we have an object method called create
.
Just before we add this to our current example, let’s look at how we can do this with a regular, standalone object. First, add any object to the bottom of our script:
let product1 = {
title: "an amazing blue shirt",
description: "blue shirt",
};
Notice here this title
and description
are generic, and could apply to multiple blue shirts, and we could just change some details such as the brand or size.
Then create our second product using an Object method called create()
:
let product1 = {
title: "an amazing blue shirt",
description: "blue shirt",
};
let product2 = Object.create(product1);
console.log(product2);
We pass in product1
to the Object.create()
method, and store this into a new variable called object2
. The console will have the following output from product2
:
{}
[[Prototype]]: Object
description: "blue shirt"
title: "an amazing blue shirt"
[[Prototype]]: Object
Initially we see an empty object for product2
, but if we open the Prototype
we have access to the title
and description
. And this is how the create()
method works. It creates a new object, and uses the existing object we pass in as the prototype.
As we discovered in the previous lesson, notice how we have multiple Prototype
properties since this is a chain. We have our own first, then last, we have the ones we inherit at the top of the chain from Object
.
As we have already learned, even though our own object is empty, we can access these inherited properties from product1
by their name:
console.log(product2.title);
We can also add our own properties and methods as you would expect:
let product2 = Object.create(product1);
product2.size = 'small';
console.log(product2);
We can now remove this small example and go back to our original starter code:
function User(first, last, occupation, lives) {
this.firstName = first;
this.lastName = last;
this.occupation = occupation;
this.lives = lives;
}
const chris = new User("Chris", "Dixon", "Dev", "UK");
const homer = new User(
"Homer",
"Simpson",
"Safety Inspector",
"Springfield"
);
This is a little different since we have a constructor function rather than a single object. If we do the same as before like this:
let copy = Object.create(User);
console.log(copy);
This will make a copy of the function which is not what we want.
Inheriting from constructor functions
What we are looking for is a way to create more constructor functions which inherit the properties of User
, let’s start with the Character
:
function User(first, last, occupation, lives) {
this.firstName = first;
this.lastName = last;
this.occupation = occupation;
this.lives = lives;
}
function Character() {}
Then we also need to pass in any extra information we need for this type of object, such as the show they are from:
function Character(show) {
this.show = show;
}
When we create a new Character
object, we not only want the show
property, but also the firstName
, lastName
, occupation
and lives
properties from the User
.
The call()
method
For this, we have a method available on the User
function called call()
:
function Character(show) {
User.call();
this.show = show;
}
The call()
method is available on a functions prototype. It allows for a function belonging to one object to be called inside of a different object. Meaning we can call our User
function from inside this Character
function.
The call
method is not exclusive to this use case, it can be used anytime we want to access a function, or a method located on another object.
The User
function also takes in these parameters:
function User(first, last, occupation, lives)
We also need to copy these over to our Character
function where we call it:
function Character(first, last, occupation, lives, show) {
// Add user properties
User.call(first, last, occupation, lives);
this.show = show;
}
Using the Character
function
Then modify our homer
variable to use this new Character
constructor, rather than User
. Also pass in the value of the show
the character is from:
// 1. Use Character function rather than User
const homer = new Character(
'Homer',
'Simpson',
'Safety Inspector',
'Springfield',
// 2. also add in the shows value:
'The Simpsons'
);
console.log(homer); // 3. log to the console
And the result in the console:
Character {show: 'The Simpsons'}
show: "The Simpsons"
[[Prototype]]: Object
constructor: ƒ Character(first, last, occupation, lives, show)
[[Prototype]]: Object
This shows homer
is a Character
object, and also has the show
property, meaning homer.show
will now work:
console.log(homer.show); // The Simpsons
Inheriting properties
What about the inherited properties?
console.log(homer.occupation); //undefined
The inherited properties don’t initiall work. This is because we are missing one small detail when calling our User
function to get these values. And that is the first parameter of this
:
function Character(first, last, occupation, lives, show) {
// Add this as parameter
User.call(this, first, last, occupation, lives);
this.show = show;
}
As mentioned earlier the this
keyword can be a complex thing to understand. Here we are saying when running our function, we can access the values using this
, just as we are above in the User
function:
function User(first, last, occupation, lives) {
this.firstName = first;
this.lastName = last;
this.occupation = occupation;
this.lives = lives;
}
Now the inherited properties should work:
console.log(homer.occupation); // Safety Inspector
The instanceof
operator
If we want to check which one of our constructors created this object, we can use instanceof
:
console.log(homer instanceof Character); // true
console.log(homer instanceof User); // false
And this is how JavaScript works using objects, there are objects right from the beginning that we can inherit from that we may be unaware of.
But as we dig deeper like this, we begin to understand this is so each new object, and this refers to object types such as arrays and functions too, can have a set of helpful properties and methods we can use each time we create our own objects.
And as we just discovered, we can also take advantage of this prototype inheritance for our own objects if needed.