Object-Oriented Programming in JavaScript
In this lecture, we will learn about Object-Oriented Programming, specifically in JavaScript. We will learn how it differs from the traditional OOP and also different ways in which we can implement this paradigm in JavaScript.
In the previous lecture, we learned about the classical oop model with classes and instances created from these classes. And remember that a class is like a blueprint which is a theoretical plan , and which we use to build many houses 🏠 in the real world. In the same way, the theoretical class can be used to create actual objects, which are called instances, which we can then use in our code. And this process of creating objects from a class is called instantiation.
Earlier, I said that in JavaScript, things work a bit differently. So why did I first introduce you to classes and instances? Well, it's because we do have similar concepts in JavaScript. Hence, it's very useful first to understand the class-instance model. Plus, many people just use this terminology in the context of JavaScript. Finally, JavaScript syntax also uses some of these terms, for example, instances. And so, you really need to know what a class and an instance are.
Let's now learn how OOP works in JavaScript. In JavaScript, we have something called prototypes. And all objects in JavaScript are linked to a certain prototype object. So we say each object has a prototype. And now, here comes the magic 🪄. The prototype object contains methods and properties that all the objects that are linked to that prototype can access and use. And this behavior is usually called prototypal inheritance.
Just note that this inheritance differs from the inheritance we discussed in the previous lecture. In the previous lecture, we had a class inheriting from another. But in this case, it's basically an instance inheriting from a class. Just keep that in mind 😉.
We can also say that objects delegate behavior (method ) to the linked prototype object. So besides prototypal inheritance, we also call this mechanism delegation. This, again, is another difference from the classical inheritance that we talked about in the previous lecture, where the behaviors (methods) are actually copied from the class to all instances.
We have actually already seen this mechanism in action many times before, but without knowing it was happening. For example, let's consider the below code:
const numbers = [1, 2, 3, 4, 5];
const doubledNumbers = numbers.map((n) => n * 2);
Each time we use an array method like the above map
method, we are able to use it because of prototypal inheritance. You will also notice that when you go to MDN to check the documentation of any array method, what you will see there is that it is actually called Array.prototype.map
. The question now is, why is that relevant? Well, Array.prototype
is the prototype object of all the arrays that we create in JavaScript. Just like the above numbers
array.
This prototype object contains all the array methods, including map
.
Since Array.prototype
is the prototype object of the numbers
array, it means that numbers
is linked to that prototype. Hence, it has access to all the methods defined in the Array.prototype
object, just like the map
method. So, we can say our array inherits the map
method, or we can say that our array delegated the behavior of mapping to its prototype.
I guess right now you have tons of questions in your mind, like, how do we actually create prototypes? How do we link objects to prototypes? Or How can we create new objects without having classes from which we can instantiate objects? So, in summary, the question is how do we implement Object-Oriented Programming in JavaScript in practice? And the answer is, in JavaScript, there are actually three different ways of doing this.
Constructor functions are a way of creating objects programmatically, using a function that will also set the new object's prototype. And this is how built-in objects like Array
, Maps
, or Sets
are implemented. Also, this is how OOP has been implemented in JavaScript for a long time.
The ES6 release introduced classes into JavaScript. And this is the modern way of doing OOP in JavaScript. However, keep in mind that these are not the kind of classes that we talked about in the previous lecture. They are instead so-called synthetic sugar 🍬 over constructor functions. So this means that ES6 classes are basically just a layer of abstraction over constructor functions.
This technique is basically the easiest and most straightforward way of linking an object to a prototype object. However, it is not as used as the previous two techniques, as we will see later.
To finish, one important thing to keep in mind is that the four principles of Object-Oriented Programming that we learned in the last lecture are still valid and important with prototypal inheritance.