7

I do a lot of full-stack JS work, and I generally follow an approach like this when creating a file with encapsulated logic:

export const SOME_KEY_TO_STATE = 'some-key';
export const ANOTHER_KEY_TO_STATE = 'another-key';

let moduleState = {};

export function modifyState(someArg) {
  // ... do some logic
  // ... perhaps derive some new value based off of logic
  const newValue = derivedNewValue;
  moduleState[someKey] = newValue;
}

export function getSomeState(aKey) {
  // ... do some sanity checking?
  const initialValue = moduleState[aKey];

  // ... calculate value based on some conditions?
  const finalValue = calculatedValue;
  return moduleState
}

I've also used classes occasionally, which essentially provide the same structure, except the module state would be inside the class as an instance variable, and possibly static variables for those exported constants:

export default class SomeThing {
  static SOME_KEY = '';

  state = {};

  modifyState(arg) { ... }
  getSomeState() { ... }
}

My preferred approach is the first one, primarily because I can import only what I need in other parts of the code without having an entire object floating around (along with it's state and other methods that I may not use <-- is this statement accurate or am I completely off?). Also, if I want to reference functions within a context, I can always do import { * as someName } from myModule

I'm curious though, is there a benefit that I'm not aware of to using a class rather than the first approach I outlined?

duxfox--
  • 8,314
  • 13
  • 50
  • 112
  • 1
    It is a personal opinion on why A is better than B – epascarello Nov 22 '19 at 21:16
  • There are so many trade offs and benefits to both. There is not really a sure fire way to say one is better than the other unless you give a direct, concrete use case. One could preach the benefits of using classes and then have to write a whole serialization helper to use those same classes with Redux. At the same time someone else might write a factory that makes POJOs but then has to import a million helper functions that might serve better as methods or even mixins. Please narrow the focus of this question. – zero298 Nov 22 '19 at 21:23
  • I think it's worth mentioning that classes can be extended and polymorphic. Classes help the code to adhere to S.O.L.I.D. principles, particularly the "L" (Liskov substitution principle): "Objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program." – benbotto Nov 22 '19 at 22:32
  • @epascarello yea, i agree, that's why i didnt ask which is better, rather if there are benefits to one over the other; and by benefits, i mean concrete/quantitative performance or optimization benefits – duxfox-- Nov 23 '19 at 02:39
  • Does this answer your question? [Differences between creating a new class to using export const](https://stackoverflow.com/questions/39076190/differences-between-creating-a-new-class-to-using-export-const) – Heretic Monkey May 06 '21 at 14:11

3 Answers3

4
let moduleState = {};

This is global state, which is best to be avoided. While it might be encapsulated well in your module, it is a static variable, essentially a singleton - it exists only once in your entire application, being created when the module is loaded.

There are some cases where this might be appropriate, but in general don't use singletons!

A class solution has the clear advantage that you can instantiate it as many times as you want, in any place where you want (e.g. tests). So make sure to understand this difference, then choose the appropriate pattern.

I like the first one, primarily because I can import only what I need in other parts of the code without having an entire object floating around (along with it's state and other methods that I may not use)

This is pretty off indeed. You always have the complete module object and state and functions floating around in your application, it's not like they get optimised away. Sure, when you don't import them explicitly they're not around in your scope, but having a class instance available doesn't really hurt either. If you don't call all of its methods, it's just like not importing all functions from your module.

The syntactic difference between calling a plain function or invoking a method on an object really is a minor detail that should not affect your decision. You can always sidestep it anyway by namespace-importing the module or destructuring the object.

Bergi
  • 513,640
  • 108
  • 821
  • 1,164
  • It's unclear whether you're suggesting that classes _in general_ are better or whether you're stating _that particular class_ is better. Certainly you can accomplish re-usability without classes by exporting factories that return methods within closures to encapsulate state. This answer doesn't necessarily have to be about "classes vs. functions" per se. – Patrick Roberts Nov 22 '19 at 22:23
  • @PatrickRoberts All I can tell that this particular use of functions in modules was not reusable, and that a `class` in general is (as long as you export the class, and not a "singleton" instance only). I don't know how important reusability is in the OP's case, I can only generalise. And yes, of course you can write reusable functions too, like factory functions that create and return a stateful object with methods/closures, and can be used similar to a class constructor. – Bergi Nov 22 '19 at 22:27
1

Well this is up to debate but I don't think there are any advantages. This question has been discussed quite a lot amongst other developers and it boils down to personal preferences.

The main reason for classes is to allow developers who work mainly with other languages to jump over to JS more easily.

As one of the developers on Quora has said:

Using functions, factory functions, old-fashioned JS classes, or ES6 classes will all achieve the same thing, and compliments of browser-level optimizations, many of these options compile down to the same code when run by the engine.

Here's a link to his answer.

Another thing is that OOP is an expression of design, it's all about structure.

Many of the modern JS frameworks such as Angular, React or Vue heavily empathise on well structured code. ReactJS even has class components containing a constructor which yet again leads to the idea of writing more OOP JS.

Even of you are talking about using classes when developing NodeJS applications, it comes down to personal syntactic preferences because V8 does most of the heavy lifting for you. Classes in JS are just a syntactical sugar coating around objects (which pretty much everything is in JS) and the V8 engine translates those for you.

Here are some good reads on the topic:

  1. Should we use classes in JS
  2. How JS works inside the V8 engine.

I hope that helps a bit.

ZombieChowder
  • 1,085
  • 10
  • 33
1

the below does not directly apply to my question because my specific example has loose functions with side-effects (aka, they mutate a state object). in this case, treeshaking will not happen even though the functions are named/loose functions instead of a class.


there is one advantage of using the first approach (loose functions) over the second (class/objects), and that's if you're using webpack and treeshaking.

with the second approach (class/object) webpack can't treeshake any part of the class and so the entire thing is imported (so a class with 5 or 10 methods will be imported fully with all 5-10 of its methods).

The loose functions on the other hand can be treeshaken as long as you only import what you need. So a file with 5 - 10 functions will not be fully imported if you only import the parts you need:

import { only, what, iNeed } from 'module';

there's obviously a little more to it than that, for more context: https://webpack.js.org/guides/tree-shaking/

duxfox--
  • 8,314
  • 13
  • 50
  • 112