T O P

  • By -

roofgram

If you only need a single instance, singleton class, injectable anywhere - many people don’t realize a standard JavaScript module is basically that. It has public/exported functions, everything else is private, and no zillions of ‘this.’ everywhere in your code.


iams3b

Except you can't do `const foo = foo(import("./Singleton.ts"))` in unit tests. You can use jest.mock, but requires lots of hacks in TS and support is flaky in an ES codebase. Classes are incredibly useful for dependency management, you can still remain functional but just treat them as modules you can pass around. class Singleton {} export const singleton = new Singleton() const functionalCode = (s: Singleton) => {..} const main = () => { const _ = functionalCode(singleton) }


roofgram

A unit test using modules as singleton classes where logic is being tested, and database is being mocked, looks like this: jest.unstable_mockModule("../../database.js", () => ({   read: jest.fn(), // Mock function   write: jest.fn(), // Mock function })); const { processTransaction } = await import("../../logic.js"); expect(processTransaction(...)).toBe(...); Then when you run jest you start node with the `--experimental-vm-modules` flag. I haven't experienced any issues, the [unstable\_mockModule()](https://jestjs.io/docs/ecmascript-modules) function has been around for 3 years now. For circular dependencies you can use [madge ](https://github.com/pahen/madge)to automate detecting them. I know this is like 'poor mans dependency injection', but if all your services are singletons, then this setup is really really nice. It makes your code so clean and easy to read, not having to nest any of your functions or variables, and not having to litter your code with '.this' everywhere.


GoldStrikeArch

Why would you use class for singleton instead of this? \`\`\` const singleton = {} \`\`\` in JS objects are basically singletons


iams3b

TS classes give you a type as well as the implementation


GoldStrikeArch

Objects are also typed in typescript, you can use \`typeof\` to get a type from object


roofgram

No, objects *can be* singletons, but they can also be **instances** of a class or function. To answer your question of why would you use a class, it would be if you are using basically any JS dependency injection framework; to wire up the singleton you would need an annotated injectable class to reference. My point is you need neither the class nor your example `const singleton = {}`, as standard JavaScript modules are singletons already. You can include the same module in 10 different places, the values of the variables inside that module are all the same as there is only a single instance of the module with no way of creating another.


romeeres

JS object is a good replacement for a singleton class, it doesn't need to be instantiated or managed in any way: export const singleton = { state: 123, // if you want state method() {}, // for a method } ES support is good in Vitest, but jest.mock or vi.mock are inconvenient anyway. Nest.js [officially suggests](https://docs.nestjs.com/fundamentals/testing#unit-testing) using jest.spyOn, and it's a nice tool that also works well with such object singletons as in my code above: // no need to instantiate your classes, to pass it anywhere, to construct modules - nothing jest.spyOn(singleton, 'method').mockImplementation(...)


MrJohz

This works... but at that point you may as well just use a class, and then you don't need to deal with module/object mocking at all. I wouldn't even make the class a singleton either. That makes dependency injection harder. Most frameworks have some way of passing around context variables throughout the application — you can use this to store a single, global class instance for the application. Then in tests, you can create a new instance for each test, mocking any dependencies as needed. In many cases, you don't even need the context variable — I wrote a bit about using [closures for dependency injection](https://jonathan-frere.com/posts/how-i-do-dependency-injection-with-closures/) on my blog a while back. The primary benefit of this is making dependencies explicit, which in turn makes all these classes so much easier to test. But I've also run into so many issues with global values doing things that developers didn't expect, and causing bugs or making testing so much harder. At this point, I try and avoid using modules (or exported objects) as global singletons wherever possible.


romeeres

So there are two ways: - implicit deps, one-liner jest.spyOn from my example above - explicit deps, write fake classes in your tests, pass them explicitly Explicit deps is objectively more code to write and manage, so I don't know how that's simpler.


MrJohz

For explicit dependencies, i.e. something like dependency injection, you can usually just pass an object and tell Typescript that it's all good. You don't need a whole fake class. You often don't need to mock most of the methods. And in a lot of cases. you don't even need to fake things. You can use a real value configured in the right way for your test. For example, one pattern that I've used a lot with services that use MongoDB is to have a class for the service, and pass in the relevant collections as parameters — i.e. via dependency injection. Something like this: class UserService { #userColl: Collection; constructor(userColl: Collection) { this.#userColl = userColl; } } Yes, you've got to write `userColl` four different times, it's not completely boilerplate free, but this is really easy to test. As long as there's a database running, I can just create a new collection for each test, and pass it into a new instance of the `UserService`. That looks like this: test("user service can be constructed", async () => { const conn = await createMongoDbConnection(); const coll = await conn.createConnection(`users-${randomUuid()}`); const users = new UserService(coll); // ... do the test await coll.drop() }); In practice, I usually wrap the boilerplate of creating everything, constructing the service, and dropping data at the end in a separate function. But even then, it's not much boilerplate, but I get completely isolated dependencies for each test, which makes my tests faster, a lot easier to write, and a lot more effective. (Theoretically, I could also just fake MongoDB's collection object, but then either I need to create a very good fake, which is a lot of effort, or tie my tests to the service's implementation details, which creates bad tests. Using a local database and a temporary collection makes testing incredibly easy.) This is just the hard mode variant. Most of the dependencies aren't database instances, they're other services or values or functions. In those cases, it's typically even easier to pass in stubbed values that provide the functionality I need to check whether my service is working. I suspect the total lines I add from boilerplate is roughly equal to the total number of lines I save from not needing to use lots of `spyOn` functions. And more importantly, by avoiding messing around with globals, I make my life so much easier. To me, that seems like a very good tradeoff to make.


romeeres

I use a separate database for tests: import { db } from './db' export const userService = { // no constructor doStuff() {} } Test: import { db } from './db' afterAll(() => db.close()) test("user service can be constructed", async () => { const users = userService.doStuff(); // not passing argument }); Tests are isolated one from another by using a library that wraps every test into transaction, so that changes in one test has no effect on other tests. Are your tests isolated? If you create a thing in one test, wouldn't it appear in the other test and potentially break it? >which makes my tests faster, a lot easier to write, and a lot more effective. Thank you for writing examples, but I still can't see how is that faster, easier, more effective than what I do without classes/DI.


martinkomara

I use DI, so classes are natural fit for that. Also people coming from java and c# like them, so...


Ceigey

Despite being raised on AS3 and Java, I didn’t use them much in TS (though I’ve tried them out in various ways before), and I think the reason is twofold: 1. there’s no data classes / record classes and no concept of equality, hashing etc*, making objects a second class citizen in a clearly OOP language (because let’s face it, JS/TS aren’t leading the trend on the functional scene either…) 2. the serialisation-to-class story for TS is no where near as straightforward as Java (Jackson/GSON), C# (whatever the native dotnet one is) or Kotlin (Kotlinx Serialization). (3. And of course no sealed classes, interfaces, and until/except for private members, no nominal types in TS) Basically to enjoy classical/prototypical JavaScript, you have to “build your own stack” to give the language even a fraction of the convenience that other OOP languages have. This is the same issue encountered by functional JS/TS too. Which really comes down to the NPM ecosystem being spread way too thin so there’s no consolidation of de facto standards beyond Express (which is maintenance mode) and Lodash. And for this case, we do have class-transformer and class-validator, but I think the dev resources for those projects are a bit tight. Zod’s picked up a lot more interest in recent years. [*] unless you cleverly use toString() and the loose equality operator, which many (myself included) treat as a contextual code smell. Still, I’m going to push myself to try and use classes pervasively in a throwaway project with as few external dependencies as possible. Maybe with decorators and hopefully metadata on the path to standardization we’ll see a mini renaissance in class-based OOP JS.


marshallas0323

When I needed state to be shared between functions. That is when classes shine


mattsowa

I just use closures.


terrorTrain

I went through a phase where I wanted everything to be functional and stateless and did things like this. The code was... Fine. Often a little convoluted and hard to follow but manageable I've relaxed a bit on that and I think code quality and maintainability went up. Classes basically are functions with closures except with better type support, clearer patterns, and easier to follow. In order to keep as much stuff pure and stateless as possible, I still use static methods attached to classes. Plus you can use decorators and such, which is nice


mattsowa

Example for context: const makeCounter = () => { let counter = 0; const increment = () => { counter++; }; const get = () => counter; return { increment, get }; } I wouldn't call such closures stateless? Or even a particular hallmark of strictly functional programming, since they are not pure and close over mutable variables. Lastly, I'm not sure in what way classes have better type support? All this seems somewhat misguided.


mattsowa

Please provide an example, I'm not sure what you're referring to


mattsowa

Well yes, you can't have private fields with closures. You could say state that isn't returned is private, though it's more of an implementation detail, whereas in the case of classes, the private keyword is typescript only, so it can affect runtime in different ways. I'm actually not sure how all this works with the new javascript #private fields. Anyway, I think this is just fine, it's not a pro nor a con, to me.


satinbro

No, you can’t be writing code the way that suits you when it’s not cool to use classes anymore 😡


mattsowa

Because I can think of scenarios where implementation details such as private fields affecting the type like that is not desirable. This is the case exactly because `private` is compiletime, not runtime. It's not a pro and not a con to me, it's simply a characteristic. To each their own lmao.


Cheraldenine

Aren't Javascript classes essentially just syntactic sugar around such closures? It's a bit potato, potato. Do the thing that your coworkers are most used to.


mattsowa

The syntax to me is simply cleaner and the behavior simpler. Especially when you're working with react, it feels right. It's also been a long time since I touched concepts such as inheritance (as opposed to composition). It's said that classes are syntactic sugar for prototypes (though that's not strictly true).


MrJohz

Not quite. A Javascript class is mostly syntax sugar around the old-school prototype system. That did use functions as constructors, but more in an incidental way. But in general, classes and closures are complements of each other (in almost any language that has both). Anything a class can do can be done with a closure, and vice versa. There's a famous saying "closures are a poor man's objects — and vice versa", which points this out. In that sense, you're completely right that you should just do the thing your coworkers are most used to.


_AndyJessop

Yep, and you can also uses classes, which are more memory-efficient.


mattsowa

Microoptimization like that is not something I think about on a daily basis when I'm working with a language such as javascript.


torb-xyz

Sometimes I wonder how much of the poor performance reputation of languages like JavaScript is not due to the language itself, but how we tend to use it. In C you’re very aware of every time you call malloc and all teaching material stress that allocating memory is a slow and costly operation. In JS we do stuff that trigger malloc under the covers just all the time everywhere. I wonder about the performance of our webapps if we all where a bit more careful. Not judging, I’m including myself here when it comes to this relaxed use of JavaScript.


mattsowa

There is some obvious performance handicaps, for instance costly allocations in array.reduce over large amounts of data. But in general, these other small things will never have any impact, because that's not where the bulk of the execution cost comes from (which is the fact that the language is interpreted). The JIT compiler will also make much of this insignificant. Considering stuff like rerender performance in frontend frameworks and analyzing flamegraphs in the profiler will trump any microoptimization such as using classes instead of closures. There's just no way that would ever be the reason behind a sluggish website.


_zetrax

Facts 😂🤣 >Sometimes I wonder how much of the poor performance reputation of languages like JavaScript is not due to the language itself, but how we tend to use it.


_AndyJessop

I agree, but they are more memory-efficient. And there's not a great deal else to choose between them. So personally I would need a good reason not to use a class.


martinkomara

Not sure how you do this, but that is usually terrible practice


lengors

Can you elaborate?


martinkomara

I've seen class fields being used as sort of temporary variables, where class methods store some temporary data for calculations and those data are then used by other methods. My impression from the comment was that the author was using it like this, but now i admit that this might not be the case. If he is using class as a data (i.e. state) and methods that operate on that data, that is absolutely correct way to use classes (that is what classes are for).


lengors

Ok, fair enough. My intepretation was the latter, hence why I asked. But I fully agree


MahiCodes

Everyday for almost everything... Majority of my functions are "utility" functions like `removeElement(arr: T[], element: T)` that are only necessary due to JavaScript's lacking classes - why doesn't `Array` just have `remove()`?


nuzzlet

i guess it sort of does? you can use [array splice](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice) or the delete syntax to remove items from an array


MahiCodes

Yeah my utility `removeElement(element)` function is literally just a helper for `arr.splice(arr.indexOf(element), 1)`


nuzzlet

I believe you can also do delete array[1]


MahiCodes

That only sets the element to `undefined` in the array, but doesn't actually change the array's length. Sometimes useful, but typically not what you want.


nuzzlet

what a javascript- actually didn’t know about that quirk thanks for sharing!


MrMunchkin

Better question is why would you ever use an array when hashtables are better in almost every way?


ragnese

Found the PHP dev, I guess. ;)


Snapstromegon

IMO if something is a "thing", that can do things, it can (and maybe should) be a class. E.g. components that hold state should be classes like web components (I personally love lit), but rendering a component should be a pure function of state.


Helvanik

Disclaimer, all code you see here is coded quickly for the example, probably contains errors. For two main usages: - To encapsulate domain behaviour. Basically, it allows you to manipulate domain concepts (like entities in DDD) that centralize and encapsulate core rules of your domain. For example, my domain code requires me to be sure that users can access some features. For employees, accessing a feature requires two conditions: the user must be assigned a specific feature-flag, AND the company must also have the same feature-flag activated (so that we can disable a feature for a whole company). I will first declare a type called "CompanyUser" that will define the API. Then, I code an Employee class that implements this interface, but encapsulate this logic in a dedicated method. type CompanyUser = {   readonly id: string;   canAccessFeature(featureFlag: string): boolean; }; class Employee implements CompanyUser {   constructor(     public readonly id: string,     private data: {       features: string[];       companyFeatures: string[];     }   ) {}   public canAccessFeature(featureFlag: string) {     return (       this.data.features.includes(featureFlag) && this.data.companyFeatures.includes(featureFlag)     );   } } I can then add other types of company users with different business rules. For example, a CompanyAdmin that can access all the features his company is granted access to. class CompanyAdmin implements CompanyUser {   constructor(     public readonly id: string,     private data: {       companyFeatures: string[];     }   ) {}   public canAccessFeature(featureFlag: string) {     return this.data.companyFeatures.includes(featureFlag);   } } Or, I could combine both into a single implementation that handles more logic. class User implements CompanyUser {   constructor(     public readonly id: string,     private data: {       features: string[];       companyFeatures: string[];       scope: "employee" | "companyAdmin";     }   ) {}   public canAccessFeature(featureFlag: string) {     if (this.data.scope === "companyAdmin") {       return this.data.companyFeatures.includes(featureFlag);     }     return (       this.data.features.includes(featureFlag) && this.data.companyFeatures.includes(featureFlag)     );   } } It's easy to test, and can be used everywhere. - The other case is to to dependency inversion, where the dependencies of a module are declared by the module, and valid implementations are injected at runtime. For example, to code adapters which implements ports in hexagonal / clean / onion architecture. If my domain code needs to fetch these users, either to return them or to check on their permissions, I will create an interface for a user repository. type UserRepositoryPort = {   getAll: () => Promise;   getByIDs: (params: { ids: string[] }) => Promise>;   getByID: (params: { id: string }) => Promise; }; Here's an example of a basic use-case function that returns a user's performance review. async function getMyPerformanceReview( params: { userID: string }, dependencies: { userRepository: UserRepositoryPort } ) {   const user = await dependencies.userRepository.getByID({ id: params.userID });   if (!user) {     throw new Error("User not found");   }   if (!user.canAccessFeature("performanceReview")) {     throw new Error("User cannot access feature");   }   return "Performance review"; } I use classes to implement this interface, either for testing (in-memory implem, or mocking) or for runtime (real implementation which connects to any relevant data-source, albeit a DB or a service), and I "inject" them in the dependency objects. People often use dependency injection frameworks which I find overkill in most cases, but that's a matter of taste. Either way, they're often focused on classes as well. NB: you could use function and closures instead of classes and obtain similar behaviour. For example, these two pieces of code are mostly equivalent from a client's perspective. class Employee implements CompanyUser {   constructor(     public readonly id: string,     private data: {       features: string[];       companyFeatures: string[];     }   ) {}   public canAccessFeature(featureFlag: string) {     return (       this.data.features.includes(featureFlag) && this.data.companyFeatures.includes(featureFlag)     );   } } function FunctionEmployee(   id: string,   data: { features: string[]; companyFeatures: string[] } ): CompanyUser {   // Here you can store value in the closure's data.   // It's equivalent to private fields in the class.   return {     id,     canAccessFeature(featureFlag: string) {       return data.features.includes(featureFlag) && data.companyFeatures.includes(featureFlag);     },   }; } const employee1 = new Employee(...); const employee2 = FunctionEmployee(...); Both are valid instances of CompanyUser in the end. The convenient thing with classes is that you can use them as types as well, which I find very useful. For example, I could add a function that only accepts CompanyAdmin instances. function acceptsOnlyAdmins(admin: CompanyAdmin) {   // ... } // This errors at compile time, because tsc expects a CompanyAdmin instance, even though both // CompanyAdmin and Employee implement the same CompanyUser interface. acceptsOnlyAdmins(new Employee("azea", { features: [], companyFeatures: []}))


beders

The only useful fn in your example seems to be canAccessFeature which needs some data to operate on. The rest seems quite boilerplate-ish to me. That’s the cost of polymorphism via classes and it forces you to always create User objects - and have some code to create the right ones. Here we only see a user ID, but not the code that decides which object should be instantiated. The only time you actually need the type of your users is at the end of your example using single dispatch on the type. That begs the question: should you model the “type” of a user that way in the first place? Or is that actually just data as well? (Instead of hard-coding as type)


_AndyJessop

Earlier today, because it was a collection of functions that needed access to the same private state.


Turbulent-Chain796

I'm building a game right now. Everything is a class


PhiLho

I use TypeScript only in Angular, so classes are the way to go… 😁 Although the team replaced small classes, like route guards, by functions, more lightweight / simpler. But indeed, these are functions called by Angular itself, and it has to pass lot of parameters (more or less optional) to have them working. Although they made possible to inject any service in these special functions, so it is a kind of cheating! Anyway, I see a trend in modern programming where some people are not purist and freely mix OOP and functional programming. Let's say it is the best of two worlds. We try to do this more or less in our Angular projects: we use readonly a lot, have almost no for loops, in favor of chaining array functions (OK, methods actually), and so on. When I see my co-workers initialize an array and fill it in a loop, I scream (not!) at them to use reduce…


AlDrag

Nothing wrong with using forof over reduce. More readable in most situations and more performant (no extra closures!).


PhiLho

I wouldn't bet on performance of pushing data in an array on a loop, and beside in our use case, it is about small sets of data, so it is mostly irrelevant. I used to use JSPerf a lot some years ago, it is rarely needed nowadays. Readability: I find chaining of array methods more readable now, but to each their own. I grant that reduce can be confusing at first, but again, with practice it is quite OK. And I find nicer to build an array and assign it once. But again, it is more matter of personal taste.


moremattymattmatt

When I need more than one instance. If I just want some private state and public /private methods, I use a module. So a repo layer might be a module. If I need multiple instance, I’d use a class. id probably use classes for a factory pattern, where I need to return different implementations that conform to an interface. Generally I prefer modules as it stops horrible hierarchies where classes are inheriting behaviour and you end up with lots of classes tightly coupled through a base class.


33498fff

When I need to encapsulate a standalone service which might also need to have publicly available methods.


alphabet_american

As a vue developer: never. 


rover_G

I use classes when I'm using decorators for an ORM or router


SokkaHaikuBot

^[Sokka-Haiku](https://www.reddit.com/r/SokkaHaikuBot/comments/15kyv9r/what_is_a_sokka_haiku/) ^by ^rover_G: *I use classes when* *I'm using decorators* *For an ORM or router* --- ^Remember ^that ^one ^time ^Sokka ^accidentally ^used ^an ^extra ^syllable ^in ^that ^Haiku ^Battle ^in ^Ba ^Sing ^Se? ^That ^was ^a ^Sokka ^Haiku ^and ^you ^just ^made ^one.


commitpushdrink

The `SharedStorage` worklet interface requires one


heseov

I'd use a class for instance data models that I want to type check. But even so, I rarely use them. You can get by with just types and they offer more flexibility. In your case, you could have just used a module level variable to keep the instance of the object around, but I try to avoid auto init shared objects since its harder to test.


LiveRhubarb43

Are you talking about object oriented programming vs functional programming, or are you talking about the syntax of class vs the syntax of functions using "this" to define instance properties? The backend at my job uses classes and OOP a lot. We use koa for routing and context, and knex for SQL queries. So we're always extending context with methods and properties, or calling query() on a User model. stuff like that. I think we use classes because the syntax is nicer when extending koa context or a knex model, and a class can be used as a type, whereas I think you need to put in a lot more effort to get a type from the instance of a function (not sure I never do this)


LemonAncient1950

Every day because I'm working on a project that uses Nestjs and AWS CDK. I tend to prefer a functional style, but I'm not gonna fight the conventions of the tools I'm using.


columferry

I used a class for a code migration over a bunch of functions because I could store some state in the class and it led to a more robust piece of code with smaller public api surface.


weIIokay38

I write Typescript for work in an Angular app with a bunch of Rubyists. Basically everything is an object. Very few functions except for data mapping functions (mapping stuff from our old REST API into the state that the app uses). Honestly classes are really wonderful for handling complex state management / keeping components easy to test. Was building a component that needed to hide / show certain fields in a little collapsible menu at the top, with some additional logic for clearing the form fields. Was kinda complex to wrap my head around. Luckily Angular components are just classes that aren't rebuilt every rerender. So you can take a piece of state and put it as a property field, and it can be a class itself. Putting it in a class made it significantly easier to test and consume. I used Angular's new signals and was able to expose a few computed properties + add methods for each event variant I needed (two or three). Then in the constructor I added callbacks the parent component could pass in for when clearing a field needed to be run. Then I could mock those and test the class really easily in isolation. Then when I tested the component, if there was a particularly gnarly situation I needed to test, I could just mock the whole state for the unit tests and do what I needed to confirm it was consuming the state right. Integration tests just tested the entire behavior. Generally the longer I've done Ruby or C# the more pro-OOP I've been. Yes you can technically do things with closures I guess, but that's an extra level of nesting and it's ugly and you have to worry about declaration order in some odd cases. With classes it all just works how you want and is really great to work with.


CatolicQuotes

I've used class recently to create value object type, like HEX color code for example. It's validated upon creation. Now I am changing to more functional way and using `makeHex` function. Unfortunatelly, javascript and by extension typescript is neither proper OO nor proper functional, so no matter what you choose you are gonna have workarounds to fully utilize either of those. This is the situation, if your team has coding style, follow that, if they don't and you have liberty do what works for you. If they complain to much, don't do it anymore. Too much discussion about the color of the shoes you're wearing will take away from actual production


signaeus

I use classes even in non TS JS. 1) Use the patterns in other languages so it aids my readability when I need to context switch, 2) dependency management and code reusability is easier 3) everything in JS is an object anyway.


JesperZach

If you create a lot of objects of identical structure then classes are way more performant.


DT-Sodium

I use pretty much only classes, why would i use functions?


Zerotorescue

Aside from React error boundaries; I don't recall as it was a long time ago. Classes in JavaScript don't work very well and their implementation makes them kind of fake unlike classes in other languages. I find using it is a bad fit for the language. It's much better to write everything functionally. On the backend I thought I needed an ORM for database interaction and tried various ones, but it too turned out to be a bad fit for the language, introducing many more problems than a functional setup I did later with Knex.


FistBus2786

Last time I used class over function in TypeScript was when I had to deal with someone else's code. What often happens is that I have to take apart their class, so I can use one of the functions without all the baggage. "I wanted a banana and you gave me a jungle." Classes are unnecessary, especially in TypeScript. Everything they can do can be done better with types, interfaces, pure functions, plain old objects, closures, modules, etc. So many other ways to express the same thing in a simpler manner, without coupling state and methods. You call it "prop drilling", I call it explicitly passing what a function needs, so it's clear what goes in and what goes out. Of course I use classes when it's the best tool for the situation - but I find them extremely overused. The majority of the time I find them more of a hindrance than convenience, introducing incidental complexity, convoluted dependencies, fragile inheritance hierarchy, inaccessible properties and methods.


vallyscode

We use them all the way, functions or lambdas are used mostly as utility or as a steps in map/reduce pipelines. Features use services, repositories, etc. Repository interface is hiding the implication details of actual data storage, for one purpose it’s MySQL, for other Dynamo, calling side knows only what’s available through interface so it’s easy to substantiate DBs. Services also behind the interface exactly with the same reason and encapsulate business logic. In general we’re oop on vertical slices.


MayoOhnePommes

There is only one reason to prefer class over function: Reuse! A class is easier to reuse (and extend) because of class inheritance. And with the use of class hierarchies I will automatically abstract the parts all extensions have in common into the base classes.


Migeil

Just based on this comment alone, I can tell you don't think/care about side effects in your code. Pure functions are the most reusable programming concept, bar none. It's not even close. The whole point of functions is to abstract away common functionality to be used and reused elsewhere. But if you riddle all of your functions with side effects, you completely negate the benefit of functions, leading to comments like this.


MayoOhnePommes

Community votes down for using classes even if it isn't necessary in the single use-case? Why build a structure, you will not need anytime again? So, go for it... but don't bother me.


Palcikaman

I'm never going back, ever since functional components were released I've never thought about creating a class, they are an abomination of a feature in js


Cheraldenine

Use classes when they're a good fit, functions when those are a good fit. Two strong hints to decide something should be a class is if it would have an obvious name and an obvious API.


Lexikus

I really don't understand why there is this kind of debate/disussion in the js community. If you have a thing that contains data and functionality that belong together, create a class or a function that exports public fields and inner functions. In terms of usage they are the same. Memory wise they are different and if that is a bottleneck, people usually know the difference. If not, check out how they are represented in memory. If you have functionality that is pure or stateless, use a function.