Do we really need classes in JavaScript after all?
Tags: discuss, javascript, webdev, webcomponents
Published: 2018-12-11 17:08:07

---
title: Do we really need classes in JavaScript after all?
published: true
description: Did classes deliver, what they promised? In this post, I share my thoughts about common classes pitfalls and possible solutions for creating better web development.
tags: discuss, javascript, webdev, webcomponents
cover_image: https://thepracticaldev.s3.amazonaws.com/i/l84bq7eu0s7dk1luz2wl.jpg
---

Among a lot of other great features, ES2015 introduced the class syntax. For some, it was a missing piece in the object-oriented programming; for others something that we should have never added in the first place. Nevertheless, classes have become beloved by the library authors and users, so today, you can find them in almost every JavaScript library or framework.

**Did classes deliver, what they promised?** Three years later, I can say that besides simpler syntax (instead of using function constructors and prototypes), they failed in various fields. Let's explore together some of the foremost pitfalls.

```javascript
class MyComponent extends CoolComponent {
  constructor(one, two) {
    // Use super() always before calling `this`
    // and don't forget to pass arguments 🤭
    super(one, two);
    this.foo = 'bar';
  }

  update(...args) {
    this.value = '...';
    // Does CoolComponent include update method or not? 🧐
    super.update(...args);
  }
}
```

**Class syntax might be confusing.** Libraries usually force users to use `extends` keyword for consuming its API. As it might look straightforward, extending requires using `super()` calls wherever needed. To be sure, that our methods don't overwrite internal ones defined by the parent, we have to be careful how we name them (soon it will be possible to use a fancy `#` keyword to create [private fields](https://github.com/tc39/proposal-class-fields#private-fields)). 

Super calls also can be tricky - for example, you can't use `this` in the constructor before calling `super()`. Oh, and don't forget to pass constructor arguments. You have to do it manually if you define `constructor` method.

Of course, we can get used to it. So we did. However, it doesn't mean that this is right.

```jsx
class MyComponent extends CoolComponent {
  constructor() {
    ...
    // Change onClick method name and forget update it here 😆
    this.onClick = this.onClick.bind(this); 
  }

  onClick() {
    this.foo = 'bar';
  }

  render() {
    return <button onClick={this.onClick}>...</button>;
  }
}
```

**Classes are tightly bounded to this syntax**. In class methods `this` represents an instance of the class. It was never intended to pass method definitions to another instance and lose that context. I know that library authors just wanted to squeeze out what's possible from the class syntax and at the same time be creative. Unfortunately, there is no one best solution for binding a function context. For the rescue, we will be able to use yet another new syntax - [class fields](https://github.com/tc39/proposal-class-fields), which simplifies creating methods pre-bounded to the instance.

```javascript
class MyComponent extends CoolComponent {
  // this method uses current state 🤨
  foo() {
    this.currentValue += 1;
    return this.currentValue;
  }
  
  // this method depends on other instance method 👆
  bar(nextValue) {
    const value = this.foo();
    return value + nextValue;
  }
}

class OtherComponent extends MyComponent {
  // Ups, this.bar() is broken now 😡
  foo() {
    return this.otherValue; 
  }
}
```

**Classes are hard to compose**. The first problem here is with stateful methods. They can use the current state and return different results, even for the same input (passed arguments). The second factor is a well-known gorilla - banana problem. If you want to re-use class definition, you have to take it all or nothing. Even if you know what kind of methods parent includes, they might change in the future, so it is pretty easy to break something.

Moreover, it is almost impossible to take out a single method from the class definition, and re-use it in another one. Methods usually depend on each other or take values from class instance properties using this syntax. Yes, there is a mixins pattern, but it does not provide a clean and straightforward way for composing classes. If you wonder, there is a [mixwith](https://github.com/justinfagnani/mixwith.js) project for that and even [ES proposal](https://github.com/justinfagnani/proposal-mixins) from the same author.

**Is there any way out from those obstacles?** Despite all of the classes burdens, they were for sure the best way to go forward in web development. The form of how we used plain objects before did not provide significant advantages over the classes. Because of that, library authors and users without thinking twice switched to them. So, is it possible to avoid all the classes problems and create a UI library, which is still powerful and easy to use at the same time?

**For the last two years, I have been working on a library for creating Web Components, which I called [hybrids](https://github.com/hybridsjs/hybrids).** As the name suggests, it is a mix of two ideas - classes and plain objects. However, the final solution did not come to me just like that.

Initially, I followed common patterns, like other libraries. I built my API on top of the classes. Although, the primary goal of the library was to separate business logic from the custom element definition and let users avoid some of the classes problems (for example `extends` and `super()`). After a year, I almost finished my work, and I was ready to release a major version. The only last thing that bothered me a lot was a lack of composition mechanism. At the same time, I began to learn more about functional programming, and I liked it very much. I was sure then that class syntax was a blocker. I tried to study a lot about how to compose classes, but all the solutions were not sufficient in my opinion.

**The breakthrough can only occur if you give up the available solutions and create new ones instead**.  For me, it was a mind-shift in how we can define components.  All those problems have become an impulse to start the process again, but this time in a completely different way. Instead of using existing ideas, I started with an empty file where I tried to create a public API example, which solves those problems. Finally, I ended with something similar to this:

```javascript
import { html, define } from 'hybrids';

function increaseCount(host) {
  host.count += 1;
}

const SimpleCounter = {
  count: 0,
  render: ({ count }) => html`
    <button onclick="${increaseCount}">
      Count: ${count}
    </button>
  `,
};

define('simple-counter', SimpleCounter);
```

There is neither class nor this syntax, only simple values and pure functions in the definition inside of the plain object. Moreover, objects definitions can be composed with ease, as they are maps of independent properties. Custom `define()` function creates a class dynamically, applies properties definitions on the prototype and finally defines a custom element using Custom Elements API.

At first, I thought that it is impossible to implement API like this in the way, that it would scale and allow building complex components with more logic than a simple counting button has. Still, day after day I tried to create better ideas and solutions to make this possible.

The hard work paid off. In May 2018 I released a major version of the library. The code, which you can see above is a [fully working example](https://stackblitz.com/edit/hybrids-simple-counter?file=simple-counter.js) from the documentation! All of this was only possible because of a number of ideas used together, like property descriptors, factories, and property translation, as well as cache mechanism with change detection.

**However, what about the opening question from the title?** Do my ideas are the answer? Time will tell. For now, I would be happy to discuss this topic with you 💡. 

{% github https://github.com/hybridsjs/hybrids %}

-------

**Do you want to know more?** In my upcoming posts, I will explain in detail all of the core concepts of the hybrids library. For now, I encourage you to look at the [project homepage](https://github.com/hybridsjs/hybrids) and [official documentation](https://hybrids.js.org).

You can also watch my [Taste the Future with Functional Web Components](https://youtu.be/WZ1MEHuxHGg) talk, which I gave at the [ConFrontJS](http://confrontjs.com) conference in October 2018, where I explained how I came to those ideas.


---------

🙏 **How can you support the project?** Give the GitHub repository a ⭐️, comment below ⬇️ and spread the news about hybrids to the world 📢!

-------

👋 **Welcome dev.to community!** My name is Dominik, and this is my very first blog post ever written - any kind of feedback is welcome ❤️.

<small>Cover photo by [Zach Lucero](https://unsplash.com/photos/qAriosuB-lY) on [Unsplash](https://unsplash.com)</small>