Skip to content

Instantly share code, notes, and snippets.

@indiesquidge
Last active January 17, 2024 09:30
Show Gist options
  • Star 41 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save indiesquidge/f8c486795d7dd455c0327ce7e0aa8c16 to your computer and use it in GitHub Desktop.
Save indiesquidge/f8c486795d7dd455c0327ce7e0aa8c16 to your computer and use it in GitHub Desktop.
We are better off avoiding ES6 classes in JavaScript when possible

Plain JavaScript objects are better than classes when they can be used, and many popular modern frameworks have adopted their use.

Consider that in React a component can be created as either a class or as an object.

// using a class
class Welcome extends React.Component {
  render() {
    <h1>Hello, {this.props.name}</h1>
  }
}

// same thing using a factory function
const Welcome = (props) => <h1>Hello, {props.name}</h1>

Both options result in the same use

const element = <Welcome name="Connor" />

But notice how much easier to understand the factory function is:

  • there is less boilerplate code
  • there is no rigid hierarchy that ties classes to super classes
  • there is no confusion around where props comes from (no inheritance "magic")
  • there is no this, therefore no broken context switching

The factory function is preferred when it is possible. Creating classes for components are only needed when a component must manage it's own state. Thus, the two paradigms co-exist in many applications.

In this way, classes and plain objects are not barred against each other––using an object is not necessarily a deviation from a pattern.


Pitfalls of JS Classes

Classes require you to do a classification: you have to look at all the objects in your application and determine what's in them (a useful thing to do), and then you have to make a taxonomy, figuring out how all those classes are going to be related to each other (what's going to inherit what, what's going to implement what, etc.), and this is usually done at a point in the project when you have the least understanding of these classes actually work, so you almost have to get it wrong. And once you get it wrong, the wrongness starts to propagate into all the upper layers, and you find yourself wishing you had multiple inheritance or other things to help you deal with the fact that your taxonomy is broken. It eventually gets so bad that you have to refactor, which is both labor-intensive and error-prone.

It turns out that when you get rid of classes, all of that goes away.

  • this creates binding issues, especially when you are dealing with promises, DOM events, callbacks, or any other asynchronous code
  • private variables and methods cannot truly be created, which opens up the potential for leakage
  • unlike classical OO languages like Java and Ruby, the class keyword isn't even really a class in JavaScript, it's just a function and some methods put on that function's prototype (this is because JS was never created as a classical OO language, but rather a prototypal OO language)
  • classes create brittle, single-parent hierarchies, which leaves no room for composition and makes changes harder in the future
  • classes obscure data by copying it and tying it closely with logic, which makes changes harder, makes testing harder, and facilitates side-effects

Using Objects when possible rectifies all of these issues.

I highly recommend we use objects when possible. This is not to say that classes are completely evil and should never be used, it should just be noted that they have pitfalls that can be dangerous over time. There is very little reason to use them when objects can be used instead.

Recognizing the advantages of objects will result in cleaner, better code.


Classes in JavaScript are particularly pernicious, especially since they are now in the standard (one of the few things I believe ES6 got wrong, especially since it further encourages the development of class-based architectures in JS applications, which is brittle and tends to incorrectly predict the future). To be clear, this is only because JavaScript as a language was never designed to be classically object-oriented––the class keyword of ES6 is simply syntactic sugar around a function. The reason I tend to avoid classes is because JavaScript is awesome enough to offer far better alternatives, such as object composition and prototypal inheritance.

In effect, JS classes are a fine starting place as they are now standardized and the easiest to understand from an OO perspective, but I would love for us to also learn about composition and the different kinds of prototypal inheritance. I am not holding a grudge against classes being used in our application, I just believe that when object creation can be done without using the keywords class, this, and new, the better off that piece of code will be (including from a testing perspective).

I am working on creating small-scale examples to show off the benefits of these approaches. In the interim, I highly recommend watching this video on Composition over Inheritance and reading this article on the Different Kinds of Prototypal Inheritance.

Pitfalls of Prototypal Inheritance

Principle benefit is memory conservation—the advantage of using Object.create instead of Object.copy is that you save memory. That may have made sense in 1995, but we have literally gigabytes in our pocket. Unless you're making hundreds of thousands of instances, worrying about how many bytes are allocated to each object is just not worth thinking about anymore.

There is still confusion between own and inherited properties, and that confusion can cause bugs and errors.

Exhibits retroactive heredity: what an object inherits can be changed after its creation

@blachawk
Copy link

blachawk commented Mar 1, 2018

In my opinion prototypal inheritance should be the first thing new JavaScript developers learn. Knowing how the core of the language works should be worth investing in. We would see less developers complaining about how the language "should" work for them.

Thanks for sharing

@emilionavarro
Copy link

Hey, did you ever get around to creating small-scale examples? I'd love to see more from you! This was a cool read.

@jpbochi
Copy link

jpbochi commented Apr 9, 2019

@eldoy
Copy link

eldoy commented Jun 4, 2022

Agree 100%. You can design literally anything in javascript using only functions. 'this', 'new' and 'class', as you said, is just bloat. I've never even used prototypal inheritance, not even that is necessary.

@surfinky
Copy link

surfinky commented Nov 1, 2023

Agree 100%. You can design literally anything in javascript using only functions. 'this', 'new' and 'class', as you said, is just bloat. I've never even used prototypal inheritance, not even that is necessary.

I second that.

@qiulang
Copy link

qiulang commented Jan 17, 2024

I came across your article and I fully agree. I make some simple rules for my team members when not to use class in js,

  • You only instantiate your class once (not to confused with singleton)
  • You just use class as a namespace for your methods
  • When this gives you headache
  • Your object does not (really) require private properties to work, i.e. your object does not need to manage/maintain internal state.
  • You have minimal public methods

I ask a question at stackoverflow about what other rules I can use. But sadly, not only does my question got many downvotes and it even got enough delete votes and it was deleted in less ONE day.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment