Please stop using classes in JavaScript

For years, OOP (object-oriented programming) was the de-facto standard in software engineering. The concepts of classes, polymorphism, inheritance, and incapsulation dominated and revolutionized the development process. But everything has an expiration date, programming paradigms included. In this article I will talk about why were classes introduced in the first place, why it is a bad idea to use classes in JavaScript, and what are some of the alternatives.

I am not going to talk about why OOP is fading away in general, but you can check out this great article for more info.

Pre-ES6 classes

Even though the class keyword was added to JavaScript since ES6 (ECMAScript 2015), people were using classes earlier. The way to achieve this was constructor functions and prototype delegation. To show you exactly what I mean, I am going to implement the same class in ES5 and ES6 environments. Consider a class Car and SportsCar that inherits Car. Both of them have make and model properties and start method, but SportsCar also has the turbocharged property and overrides the start method:

As you probably guessed, the Car (line 2) and SportsCar (line 18) functions are constructor functions. The properties are defined using this keyword and the objects themselves are created with new. If you are not familiar with prototype, this is a special property that every JS object has to delegate common behavior. For example, the prototype for array objects has the functions you probably know well: map, forEach, find, etc. The prototype for strings has functions replace, substr, etc.

After the Car object is created on line 33, you can access its properties and methods. The call to start on line 34 results in the following actions:

  1. The JS engine asks the car object for a value with the key start.
  2. The object responds that it has no such value
  3. The JS engine asks the car.prototype object for a value with the key start.
  4. the car.prototype returns the start function, that the JS engine executes immediately.

Accessing the make and model properties are performed similarly, except they are defined on the car object directly, instead of the prototype.

Inheritance is a bit trickier. It is handled on lines 24-25. The most important function here is the Object.create function. It accepts an object and returns a brand new one, with its prototype set to whatever was passed as an argument. Now, if the JS engine does not find a value on the sportsCar object or sportsCar.prototype, it will consult sportsCar.prototype.prototype which is the prototype of the Car object.

ES6 Class keyword

With the release of ES6 in 2015, the long-awaited class keyword arrived in JavaScript. It was done as per numerous requests by the community because people were feeling uncomfortable coming from object-oriented languages. But they missed one important point.

JavaScript has no idea what classes are

JavaScript is not an object-oriented language, it was not designed to be one, the notion of classes is absolutely not applicable to it. While everything in JS is indeed an object, these objects are different from the ones in Java or C#. In JS, an object is simply a Map data structure with a somewhat sophisticated lookup procedure. That is it, really. And when I say everything is an object, I mean it: even functions are objects. You can check it with this snippet:

Ok, this is all good, but how does the class keyword work then? Glad you asked. Do you remember the Car and SportsCar example earlier? Well, the class keyword is simply syntactic sugar on top of that. In other words, class produces conceptually the same code and serves only aesthetic and readability purposes. As I promised earlier, here is an example of these same classes in ES6:

These examples are identical and produce the same result. What is interesting, they produce (almost) the same code under the hood. I will not write it out here, but if you are curious, head out to the online Babel transpiler and have a look at the output.

Why not?

Now you should have an understanding of what classes in JS are and how they work. Now, with all this knowledge, I can explain why using classes in JS is a bad idea.

  1. Binding issues. As class constructor functions deal closely with this keyword, it can introduce potential binding issues, especially if you try to pass your class method as a callback to an external routine (hello, React devs 👋)
  2. Performance issues. Because of classes’ implementation, they are notoriously difficult to optimize at runtime. While we enjoy performant machines at the moment, the fact that Moore’s law is fading away can change all that.
  3. Private variables. One of the great advantages and the main reasons for classes in the first place, private variables, is just non-existent in JS.
  4. Strict hierarchies. Classes introduce a straight top-to-bottom order and make changes harder to implement, which is unacceptable in most JS applications.
  5. Because the React team tells you not to. While they did not explicitly deprecate the class-based components yet, they are likely to in the near future.

All of these issues can be mitigated with JS objects and prototype delegation. JS offers so much more that classes can ever do, yet most developers are blind to it. If you want to truly master JS, you need to embrace its philosophy and move away from dogmatic class-based thinking.

Get new content delivered to your mailbox:

leave a comment