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.
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.
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
I second that.