DEV Community

Stop Using JavaScript Classes!

Johnny Araujo on March 30, 2022

Are you mired in a JavaScript codebase full of classes? Are you new to JavaScript and tempted to reuse patterns from object oriented languages? D...
Collapse
 
raslanove profile image
raslanove • Edited

Ummm. I don't see why you are trying to avoid classes, they are useful and as you mentioned, soon they will be getting more powerful. And you haven't replaced classes with modules, you replaced them with closures. And after removing the comments and blank lines, both implementations of your book class/closure took the same number of lines.

Seriously, your point flew over my head. You are replacing classes with something less flexible, with no gains at all. Modules are nice, and they can be used to wrap classes just like they wrap closures. So, the module point is out of the window.

And about being "more behaviorally consistent", "more pleasant to maintain", "less cumbersome to write", and "easier to read". Really? In what way? Shouldn't you have explained these before concluding them?

Instead of telling people not to use classes, this would have been a nice article about closures and how they can be used for those who have class-phobia.

Collapse
 
jaraujo6 profile image
Johnny Araujo

Thanks for reading! To be honest I care less about how people code and more that they understand the language that they are coding in. I tried to bring that point home in the end so sorry if that didn't come across. I'll do better next time ;)

Collapse
 
greggcbs profile image
GreggHume • Edited

I use functions over classes and I like it. I dont see the need to write a class in nodejs for my use cases.

To use a method in a class you need to import the entire class in the file you are wanting to use the method... modules allow you to pick and import specific functionality.

I like the fact that classes scope functions product.get() or product.create() - but I use good naming conventions on my functions productGetAll() and productCreate() and my IDE loves that because when i start typing 'prod' it gives me a list of all available methods for products - and this works for my db queries, router, utils etc.

For me its about developer experience and patterns to make coding in my projects easy. I just dont see how classes would benefit me with the code i write - but that doesnt mean i avoid them, i just use what I feel is best for my projects.

Collapse
 
metcoder profile image
Carlos Fuentes

Hey! Nice work with the Article, but I think the code focus way to much on high level implications rather than low-level ones. Meaning the implications of using Classes and how they really relate with JS prior ES6.
Overall, Classes came to allow more people easily start using the concept of Object without fully needing to understand/learn Prototype Oriented Programming which is what JS implements under the hood. Along with that, Classes does pretty more couple of things that easies the need for dealing with this and for instances Classes and Instances as well.

The Article quite implies that modules are better than Classes which is not the case, and they shouldn’t be compared as are totally different things. A more real comparison is to construct Objects out of Functions and export them, or use Prototype for attaching methods and more.

Again, will be better to compare it to other things instead of directly against modules :)

Collapse
 
jaraujo6 profile image
Johnny Araujo

Hi Charlie, thanks for reading! That focus was intentional because users such as yourself have a better low level grasp and aren't as quick to use these patterns. In my honest opinion if you know what you're doing, you can do whatever want, but many people have limited exposure and tend to run with whatever they've seen.

Regarding classes vs functions with methods, that's exactly how JS classes work under the hood so it wasn't an alternative I was interested in exploring. Good point though!

Collapse
 
metcoder profile image
Carlos Fuentes

Hi Johnny :)

Sure you’re right on your first point, for new adopters it can be easy pick this approach mostly if your coming from a OOP language background like Java or C# (which are the most common), and I also do agree that as long as you’re confortable with what you’re doing it, should not be a problem at all :)

Sure! I felt it like more natural to compare as even that the JS Modules kinda can work like this, sometimes new adopters can confused them with what the Classes are really trying to achieve in JS. Thanks a lot for the feedback! Keep it going 🙌

Collapse
 
smpnjn profile image
Johnny Simpson • Edited

I actually think it's more confusing having two paradigms. I sometimes open Javascript files that are written as classes and it takes me a little it of time to understand exactly what they were trying to accomplish since I'm used to working differently in JS. I'm sure it's the same if a OOP programmer opens a prototype/object based Javascript file.

Anyway, I'm not even sure classes in Javascript really give OOP programmers all the flexibility that classes in other languages do. Fortunately that's changing with the inclusion of things like decorators. Since classes in Javascript are purely syntactic sugar, I also wonder what extra you are getting? It's not like there's extra functionality that comes with classes.

Collapse
 
jaraujo6 profile image
Johnny Araujo

Hi Johnny! Thanks for reading! It's definitely best if teams discuss beforehand which patterns to use and which to avoid. Consistency I find is usually more important than the patterns you end up sticking with.

Collapse
 
raslanove profile image
raslanove

Well, object oriented is a pretty strong and established programming paradigm. And it was introduced mainly for code organization! And object oriented is not about classes. It's about encapsulation, polymorphism and inheritance. The alternative that the author is suggesting is STILL OBJECT ORIENTED! Just a different syntax. If he was suggesting a different paradigm, like function or data oriented, maybe the article would have been more useful.

The reason why the article is getting heated comments is because the author is asking for them. You can clearly see that from the graphics used in his article. Besides, the use of strong language, like "Don't use classes" gives you the feeling that you are going to behold a great change. In a field where there's no absolute right and wrong, you expect someone telling you not to do something to have a pretty strong argument. Honestly, I feel like I was click-baited.

Collapse
 
assertnotnull profile image
Patrice Gauthier

Classes in Javascript are very basic. The real benefit is DI when using decorators with Typescript. In one of my previous work I saw that if there's no clear structure in place it becomes a mess and over time I feel now that JS development suffers from a common structure, conventions. At scale the benefit of DI becomes useful but then when you reach that stage then you already have a mess that is too big to tackle and not worth it business wise. Developers create low quality code as result and best practices are an ideal in theory. Functions with 600 lines were looked over like it's fine and the company was just 5 years old. We really need to use something like Nest.js.

Collapse
 
jaraujo6 profile image
Johnny Araujo

Nest.js is one of the few exceptions I make when it comes to using classes in Node. Thanks for reading!

Collapse
 
kulaggin profile image
KulaGGin

DI becomes useful immediately with 0 lines of code. The mess will slow you down in minutes, not years. There is no excuse not to use best practices such as TDD and applying design patterns even for personal projects.

Collapse
 
x1unix profile image
Denis Sedchenko

You definitely can replace classes with modules in the same way as you can just use static classes/methods in Java/C#/etc instead of object instances, but...

DI allows you to achieve loose coupling in your codebase and convenient mocking of different subsystems in your project's unit tests.
This very helps if you're working on a relatively large monolith project.

Collapse
 
jaraujo6 profile image
Johnny Araujo

If it's the right tool for the job, by all means. Thanks for reading!

Collapse
 
rtmann profile image
rtmann

I've gone back and forth with classes, however I absolutely love typescript and we use it exclusively and as such I've come to appreciate classes more especially when paired with MobX. Mobx can automatically bind class methods and I basically never have to worry about "this context" being hijacked, it's just not a thing. And even if you want to create a method on a class that isn't a mobx action or flow, you can still enumerate all the functions in a class and automatically bind them all.

And with Stage 3 decorators coming classes are about to get really amazing. And writing little functions like "createBook" that returns it's methods/getters etc is really troublesome.

In typescript I can do this

export class Thing extends BaseThing {
    private static _currentInstance: Thing | undefined;
    @observable private _someObservable: string | undefined = undefined;

    protected constructor() {
        super(...argsHere);
        makeObsevable(this, undefined, { autoBind: true }
    }

   public static get currentInstance() {
        if (Thing._currentInstance === undefined)
            Thing._currentInstance = new Thing();
        return Thing._currentInstance;
    }

    protected override SomeOverride() {
        super.SomeOverride();
    }
}
Enter fullscreen mode Exit fullscreen mode

Vanilla js, yeah classes are kind of meh, you can create classes via functions, but it's cumbersome to do that in Typescript. Typescript strongly prefers using classes when a class like thing is deseriable.

Collapse
 
urielsouza29 profile image
Uriel dos Santos Souza

Very good

Collapse
 
jaraujo6 profile image
Johnny Araujo

Thanks for reading!!

Collapse
 
craigstowers profile image
Craig Stowers • Edited

I didn't want to use classes in my latest project for some of the reasons stated here, but they're actually much faster in some use cases (at least from my tests creating thousands of instances).

If you want the following features: inheritance/extending, overriding getters/setters on sub classes (key word "overriding"), then you can't beat classes in javascript.

I've toyed around for ages with factory function+mixins, and prototypes. I don't know what's going on under the hood but there are huge penalty's with non-class based patterns that allow for getter/setter overrides. Unless someone can show me a pattern that works (it's a hard topic to find information on actually), then I'm running on the assumption that browsers have optimized classes in some special way when it comes to my use case.

I'm guessing the browser translates class code to objects/prototypes for each sub class. Meaning that it builds one generator it re-references for each sub class and thus avoids going through the slow mixin process that assigns/joins objects on every instance in a factory/prototype pattern. (You need mixins to override getters/setters as far as I know, but I'd love some guidance if another way).

Collapse
 
kulaggin profile image
KulaGGin • Edited

Nice rant.

Why is this my reaction? Because in JavaScript, there exists a plethora of better ways to write code, and yet in some codebases, classes seem to dominate more than Michael Jordan in Space Jam! (Sorry, Lebron.)

There's nothing wrong with organizing your code in classes. It's actually a very good way when language supports it. I'd say it is the best way generally. Everyone understands classes. If they don't understand classes, are they even really a programmer?

Above you see a JavaScript class with only one method and one constructor argument for injecting a dependency, and the class is only ever instantiated once. And my reaction is always the same…
Image description

So? There's nothing wrong with having a class even when you create only one instance, same as there is nothing wrong with having a closure when you only have only one instance. I often enough create classes in other languages of which I only have one instance at runtime but it makes it easier to test the module(dependency injection and substitution with mocks or objects with specific state in tests).

Anyway, in your rant about classes vs closures this paragraph of yours doesn't add any value and isn't an argument against classes in the classes vs closures debate. Having one instance at runtime is just fine in either case. Moving on.

According to Oracle’s Java documentation, in object-oriented-programming, an object “stores its state in fields (variables in some programming languages) and exposes its behavior through methods (functions in some programming languages).

Having fields is not an attribute of an object in OOP. And it's easy to demonstrate. Take any interface, then create an object that implements that interface and make that object save its state in the database by calling some static functions in a namespace. Now you have an object that fulfils your requirements, implements an interface, stores its state in a database. You can program against an interface, use that object successfully, yet, that object has no fields.

Methods operate on an object's internal state and serve as the primary mechanism for object-to-object communication”.

Wrong again. Not long ago I've created a IKeyboardGateway interface with method GetKeyboardState and created an implementation of that interface: KeyboardGateway that returns an array of size 255 with the state for each key of the keyboard. The KeyboardGateway object didn't operate on an object's internal state. The KeyboardGateway object called system's GetKeyState function(Windows in this case for me).

Other modules that used IKeyboardGateway object didn't know it was implemented using the KeyboardGateway class. All the other modules knew is IKeyboardGateway and about one method: GetKeyboardState. That's where OOP is happening: in programming against interfaces. Not in creating classes with a bunch of fields and methods. You can have solutions with classes all over the place in each and every file, and have nothing other than classes, and do no object-oriented programming: all of your dependencies will be hardcoded to specific implementations.

Uncle Bob nicely explains what OOP is and what an object is here:
youtu.be/oar-T2KovwE?t=3246

There’s nothing inherently bad about using this pattern in JS. However, the problem I’ve frequently encountered is that this is sometimes the only pattern, or a frequently misused pattern, in a JS codebase.

Yeah, there's nothing wrong with using classes in JS. And yeah you can write very bad code with classes. Same as with closures.

But if the OOP pattern of classes is the only way you know how to or like to code, then of course there will be times when using it will make the code harder for others to understand because the pattern is not a natural fit for the problem and/or programming language.

That's too vague and needs some concrete example which would demonstrate that. And I doubt it will happen. If we implement the same thing with closure, it'll be just as hard to understand.

So what is an alternative to classes in JS?
JS modules, of course.

JS modules aren't alternative to classes in JS. Alternative to classes is closures. In your example later you provided alternative solution with closure, not module. Module can export either a class or a closure.

There’s less boilerplate as instance fields and methods are replaced with simple variables

False again. You don't need to declare fields in a class(outside of functions). You can just set them in a constructor, same way you did in a closure. You'll just have to add this..

and their status as public or private is denoted by their inclusion or exclusion in the return of the exported createBook function module

This is additional boilerplate code: you need to return a whole struct with variables in it.

In classes you declare private fields with this.#myVariableName and in closures you return the struct with public fields available for use. That's around the same amount of boilerplate code.

Moreover, you might not need dependency injection. Unless you are going to construct different Library instances with different DB instances, importing the DB directly into the module hosting the Library class/function will save you from having to write redundant code across multiple files.

No. It's not about redundant code. It's about managing the dependencies.

Collapse
 
brandonpsmith profile image
Brandon Smith • Edited

While everyone is hanging on to classes, massive frameworks like React (hooks) and Vue (composition api) are moving away from them? We will come back full circle once we realize that classes are not really needed, this is one of the most confusing things you can teach to a beginner, and composition > inheritance.

Collapse
 
micdropper profile image
Ethan Hahn

I actually think the author did a decent job, but this argument is the winner in my opinion though. I hate the this keyword. There's a reason most of the popular packages out there adopt a "just works" philosophy. I think languages should adopt a similar philosophy. I recently converted one of my personal projects from classes to functions and the bugs and head scratching started to fade.

Collapse
 
kulaggin profile image
KulaGGin

composition > inheritance

Wrong. They're not competing. They're just different tools for different situations. It's like saying "saw > hammer". In some situation the hammer is better, and in other the saw is better.

We will come back full circle once we realize that classes are not really needed

They're absolutely not needed. You can just create one method main and put all your global variables about it and code it like that, sure.

Collapse
 
dannyengelman profile image
Danny Engelman

I just need you to know that you have other options.
Not just in coding, but in life.

On our First Date you will probably tell me to hold the fork in my left hand.

Collapse
 
jaraujo6 profile image
Johnny Araujo

That's hilarious! Thanks for reading!

 
raslanove profile image
raslanove

Encapsulation is associating the code with the data it acts upon. It has nothing to do with inheritance. Closures do the same, they associate the code with the data.

It's ok to say we don't need classes. It's not ok to say "Hey! Don't use classes. They are inferior!". The author has shown a nice technique to avoid using classes, but he didn't state valid reasons to avoid them in the first place. Yet, he was very assertive about his stance, which some of us find less appealing. This could mislead some beginners into believing his -so far- baseless assertions. He could present his findings as another way to do things, not as the "correct" way to do things. That's the whole point I'm trying to make.

Collapse
 
jaraujo6 profile image
Johnny Araujo

This is pretty cool! I never curry as much as I would like to in practice! Thanks for reading!

Re: heated comments....it's not to bad here on dev.to but on the medium cross post....LOL

Collapse
 
kahunacohen profile image
Aaron Cohen

I don't find this a particularly strong argument. And this is coming from someone who generally avoids classes in JavaScript. That said, I don't think referencing a module-level variable is a "closure." A closure is defined as:

"A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function's scope from an inner function."

I think what is commonly meant as a closure is the private scope of a function's outer scope. A closure is another function that "closes over" that private scope. The local variables are not observable or mutatable outside the function's scope.

A module-level variable, on the other hand, is observable by either other functions in the module, and even functions outside the module (if exported).

One of the reasons your argument isn't persuasive is that I'm not sure you've done your homework.

A more reasonable way to accomplish what you are trying to do is by actually using closures and returning functions that close over private state. This is, in fact, probably what the class based syntax transpiles down to when using Babel.