It’s fun to know the real struggle we used to have in the past when there was no class keyword or at least it was not being supported widely and I remember using this keyword and prototype everywhere in my earlier Javascript days.

Nowadays, I believe we should not say that ES6 is new because almost all the browsers and compilers now support most of the ES6 features but let’s say if you’re coding something in React.js or Vue.js, you have to use plugins like Babel which transpile (not compile) the code into ES5 and which ensures that almost 100% of browsers or any other compilers/transpilers are able to execute them. That’s why it is also important to understand how these classes work in both ES5 and ES6.


TL;DR

All in one gist with all the code snippets:
https://gist.github.com/apal21/7cf1d3e62c2eae60860f2b8a97caa799


No class keyword in ES5

As I mentioned above, there is no class keyword in ES5, so you might wonder how do we use to implement class-like functionalities? Short answer — using functions. Though javascript functions were the only saviors those days, we had to struggle a lot with one of the most confusing keywords: this and now you can easily add properties without making any effort. I meet many of the new Javascript developers and they don’t know that there’s a keyword prototype exist and even if they know they haven’t used it.

So before diving into the implementation, let’s first see why and where prototype is useful.

Prototype simple Use cases

I’ll show you one simple but relatable example. We all use JSON objects in Javascript and if we want the list of all the keys of that object, we can use Object.keys(someObj):

But in Javascript, we cannot get the list of all the keys using someObj.keys(). While some other languages like Python do have this feature. And if we want, we can also implement this feature in Javascript using prototype. Let’s extend this example.

In this example, we have an object called user. If we check the type of that user we’ll get “object” in the result.

console.log(typeof user)
// "object"

We can also check the existing properties it has by doing this:

console.log(Object.prototype)

You can see that there is no function called keys inside to list all the keys for us. So let’s add this feature. We’ll assign a function to this keys property and that will return an array of the keys of an object who is calling that function using this keyword because we want this feature for all our objects.

You can do this for anything in Javascript. For example, if you want to add some feature and want to use with your string you can add that using String.prototype.someFeature = your logic.


Classes in ES6

First, let’s see the fortune we have now and how easily we can implement any functionality without compromising the readability of the code in some simple steps. We’ll create a base class Person which will have 3 fields: name, age and gender. Then there will be 2 different classes, Teacher and Student, which will extend the Person class. Teacher will have a subject and Student will have marks fields.

In this example, we are passing the arguments and the class is handling these arguments using the constructor and binding that with this so that each instance would have its independent values.

We also have a keyword extends so that all the properties of the base class automatically get assigned to the subclasses. Also, all the properties of all the classes are scoped together, which makes the code cleaner and increase the readability. You can call the constructor of the base class using super.

Note: I don’t know if you noticed this — we are not using any let, var, const or function keywords to create a property, which may lead to some confusion. And after ES6, we don’t need the constructor to initialize the properties. I’ll publish that blog after this, which will clear one doubt that all the beginners have — what is the difference between a variable and a property inside a class?


Classes in ES5

Now, we’ll see how we can implement the same thing in ES5 using functions. As we discussed earlier we can use prototype to add/extend new properties and we’re going to do the same with the functions. Here, functions will take the arguments and bind the parameters with this (keyword).

In the code above, we have to use prototype every time we want to add a new property to our object. Now in ES5, there is no extends that can extend all your the properties, no constructor for initialization or super to call the constructor of the base class.

However, the body of the main function itself acts as a constructor and you can write your logic (e.g. binding the properties with this) or call the constructor of the base class.

Calling the constructor of the base class

In the class Teacher(or Student), we’re calling the base class Person by passing this and then the other arguments.

Person.call(this, name, age, gender);

The first argument this will change the owner of the base class’ constructor and it will return the result of the calling function using specified this and the other arguments. Here this will ultimately refer to the instance of the subclass Teacher or Student.

Extending the properties of the base class

Now we all know that if we want to extend the property, we can use prototype. So in this example, this helps to do the same.

Teacher.prototype = Object.create(Person.prototype);

Here, Object.create() returns a new object with the specified prototype object and properties. It is mainly used for implementing inheritance. We’re passing Person.prototype as an argument so it will extend all the properties of the Person to the class extending Person (Teacher and Student).

Use cases

You can see there are so many different things which are needed to code just to have these features. But the real question is — is it worth the effort?

Only you can answer this question by looking at this compatibility tables. Check the difference between the compatibility of the class and the compatibility of the prototypes. If you want to support the features for all the older browsers (including IE), you should go for the prototypes.

You can also choose to use ES6 features and transpile them using the tools like babel, but if something goes wrong with the code after converting it to ES5(very little chances), it is very difficult to debug. If you want to try this, copy your ES6 code, paste it here and see the results.

Either way, it is good to have some knowledge because if any error occurs, you can solve the problem on your own or maybe contribute to the open-source community. Your thoughts and ideas are always welcomed.


Conclusion

Apart from initializing, calling the constructor of the base class and extending all the properties of the base class (which is almost everything), everything else remains the same. Also, you can extend these properties anywhere in the code as they are not wrapped inside a common scope which makes the code difficult to read and in the gigantic applications, I believe the readability of the code is one of the most important features. Generally, if you use Babel, it rarely throws an error but if it does, you can always check the list of known bugs or ask for the help on the websites like StackOverflow.


Footnotes

Let me know in the comments if there is something wrong with my understanding, or the code or I’ve missed something in this blog. You can also share your ideas or tell me how this helped you build something amazing.


References

  1. Object.prototype
  2. Function.prototype.call()
  3. Object.create()