In the previous article, we saw how we can use functions to create objects using the new
keyword. We also saw one can assign the prototype to objects created using these functions by calling Function.prototype on the function itself. However, there was one slight inconvenience that we had noticed with this method. Let us see with an example.
function User(name, score) {
this.name = name;
this.score = score;
}
User.prototype.incrementScore = function() {
this.score++;
};
let user = new User('Kabir', 5);
user.incrementScore();
As you can see above, the code for creating a User object is stored in the User constructor function. The code for setting its prototype method is in another block of code below. This might make it harder for one to debug issues with prototype tracing. Hence, Javascript had introduced the 'class' construct, which solved a lot of issues with regards to object-oriented programming in the language.
The class
construct
Let us see with a quick example of how we can use class to implement the same code as above.
class User {
constructor(name, score) {
this.name = name;
this.score = score;
}
incrementScore() {
this.score++;
}
}
let user = new User('Kabir', 5);
user.incrementScore();
In the above code, we have seen a class with the name 'User' being defined. When new User()
is called, the following things happen:
- A new object is created
- The code in the constructor function of User class is run, with
this
inside that function pointing to the newly created object. The arguments passed to the new User() call are the arguments used in the constructor function (in this case, 'Kabir' as name and score as 5). - This object is then returned.
We have also added a method called incrementScore in the class body as well. This method gets added to User class's prototype.
Before we proceed further, let us first understand how a class works in Javascript under the hood. Let us call typeof on User and see what gets returned in the console
console.log(typeof User); // prints function
The truth is that class in Javascript is somewhat of a 'syntactic sugar'. This means that, under the hood, it works in almost the same way that creating an object using a function would work. The code for class User written above actually does the following:
- Create a function called User. The function's body code is taken from the constructor method of the class.
- Any other methods present in the class are added to the prototype of User function.
Hence, in our above example, the User class had created an object with the name and score properties, as well as including the incrementScore function in its prototype.
Although class is considered a syntactic sugar, there are still a few important differences between using a class or simply a function to create objects. But we wouldn't be delving into that in this article as the focus here is on prototypes only.
Inheritance in classes
Now that we have seen how the prototype is set in a class, let us further proceed to how class inheritance works too. This is achieved using the extends keyword. Let's say we have a Bird class.
class Bird {
constructor(name) {
this.name = name;
this.canFly = true;
}
sing() {
console.log(this.name + ' is singing');
}
}
let myBird = new Bird('My bird');
myBird.sing(); // My bird is singing
Say we would like to create another class for an Eagle. But we wish to reuse the Bird class for common properties. Or in other words, have Eagle class inherit from the Bird class. This is achieved as follows:
class Eagle extends Bird {
attack() {
console.log(this.name + ' is attacking');
}
}
let eagle = new Eagle('Bald eagle');
eagle.attack(); // Bald eagle is attacking
eagle.sing(); // Bald eagle is singing
The Eagle class is defined above, along with extends Bird
. This means that the Eagle class has access to the properties and methods (including the constructor) defined in Bird class. This is because the extends
keyword tells Javascript to set the prototype of Eagle.prototype to Bird.prototype. In other words, Eagle.prototype not only has a method called attack(), but also has it's __proto__ property set to Bird.prototype. This allows objects created using the Eagle class to have access to methods in the Bird class due to the prototype chain. One can also say that the Eagle class is the child class of its parent class, which is the Bird class.
There are some other concepts in class
like the super method and method overriding that explain the behavior of child classes when created. I shall cover them and more in my next article on classes in Javascript.
This concludes my series on prototypes in Javascript. Hope you found it helpful.
Top comments (0)