DEV Community

Cover image for Replacing JavaScript Classes With The Module Design Pattern
Adam Nathaniel Davis
Adam Nathaniel Davis

Posted on • Updated on

Replacing JavaScript Classes With The Module Design Pattern

Hi. My name is "Adam" and I'm a JavaScript developer. I've been class-free now, for... Oh, wait. I think I'm mixing up my meetings.

My regular readers (both of them) know that I get annoyed by the JS community's irrational fear of the class keyword. Some on here have even told me that I love classes. That's not actually true, but I understand why some jump to that conclusion. Because unlike so many other JS devs, I don't start spitting and cursing any time I even see a class keyword in code.

But I'm here to tell you that, for some time now, I just haven't written any classes in JavaScript. At all. My React code is all function/Hooks-based. And I'm OK with it. Really. I don't even miss classes.

I'm happy moving along without classes for a few key reasons. First of all, the class keyword is nothing but syntactic sugar in JS. It doesn't provide any new functionality that you didn't already have in the language. It just makes some of that functionality... cleaner.

What do I mean by "cleaner"? Well... call me cranky, but I don't ever wanna write more .prototype-style objects, where I'm jumping through JS hoops to accomplish that which just fits under a class paradigm. But with "modern" ECMAScript standards, you really don't need to bother with all that .prototype stuff anymore. Specifically, I'm talking about the Module Design Pattern.


Alt Text

What IS It??

I know this will feel a bit basic for many of you, but first let's define what I mean when I say "Module Design Pattern". Like most discussions about design patterns, the literal definition of "Module Design Pattern" can be a bit... fuzzy. And quite frankly, trying to come to a hard-and-fast consensus can be extremely boring.

Wikipedia defines the module pattern as:

A design pattern used to implement the concept of software modules, defined by modular programming, in a programming language with incomplete direct support for the concept.

Wow. That's incredibly... unhelpful. Impossibly vague. Pretty much useless.

So let me show you what I understand to be a "Module Design Pattern", specifically as it applies to JavaScript.

export const MyModule = () => {
   return {};
};
Enter fullscreen mode Exit fullscreen mode

That's... it. No, really. That's it. Sure, most real-life examples will have significantly more detail to them. But the example above actually qualifies.

It's really just a function - that returns an object. (And yeah - the function could also return an array. That still qualifies. In JavaScript, an array is an object.)

You may be thinking that it's just a plain ol' function with some kinda-modern syntax (arrow function) and the modern export convention added to it. And - you'd be right.

You may also notice that I named the module in Pascal-case, which is not part of any standard for the Module Design Pattern. But I'll explain later why I do that.

If you don't have some sorta ingrained affinity for the code sample above, maybe it's because you're not a React developer?? Lemme explain...


Alt Text

React & the Module Design Pattern - A Perfect Partnership

In the horrible, no-good, bad-ol' days, we used to write React components like this:

export class MyComponent extends React.Component {
  render() {
    return (
      <div>Here is some JSX</div>
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

I hate even having to type that sample. It's dirty right?? You probably feel kinda ashamed having even looked at it. And god forbid if you actually wrote that kinda code... It's the kinda stuff you wanna hide from your future grandkids. I'm actually sorry that I even showed it to you here.

Thankfully, nowadays we've evolved out of the Dark Ages. We no longer write shameful stuff like that example above. Now we write our components like this:

export const MyComponent = () => {
  return <>
    <div>Here is some JSX</div>
  </>
}
Enter fullscreen mode Exit fullscreen mode

Ohhh, mannn!! Just look at that gorgeous code! It's obviously so much better! Thank god we've seen the error of our ways - and repented! Now we write our components in a way that looks a heckuva lot like... a Module Design Pattern.

(NOTE: The above example returns a bit of JSX, and in React we normally refer to this whole block of code as a "component". But a block of JSX will yield a typeof... object. So when you write a component like the one above, you're really just creating a function - that returns an object.)

And remember when I said that I (usually) write my Module-Design-Pattern functions with Pascal-case? Well, React components are named in Pascal-case. And if you look up "component" and "module" in most dictionaries, you'll have a hard time distinguishing the difference between those two terms.

Of course, I don't always use Pascal-casing. I use traditional Camel-casing when I'm writing... Hooks. Like this:

export const useSomething = () => {
  const [statefulValue, setStatefulValue] = useState('');

  return {
    statefulValue,
  }
}
Enter fullscreen mode Exit fullscreen mode

In this case, we're not dealing with JSX, but this still fits perfectly in the Module Design Pattern. We're exporting a function, which returns values that presumably represent some aspect of its own internal "state".

In other words, AFAIK, nearly every single custom Hook I've ever seen complies with the (admittedly loose) standards of what comprises the "Module Design Pattern". For that matter, nearly every functional component in React also meets these standards.

BTW, if you think this is just some snarky takedown of functions-vs-classes, it's not. Because there are some tangible benefits of the Module Design Pattern. To understand what those are, we need to appreciate how JavaScript treats functions...


Alt Text

Functions: JavaScript's Magic Boxes

Ima be honest here: It took me a number of years before I really appreciated this about JS. In JS, functions are "first class citizens". What that means, in laymen's terms, is that they can be passed around like a string, or a number, or an object. In JS (or any other language that features functions as "first class citizens"), the function isn't a subroutine, it is a value in its own right.

What does that mean with regard to our Module Design Pattern???

Well, take another look at that dead-simple custom Hook above. It's returning an object. And that object could contain... basically anything - including functions. In other languages, can you think of something that also can contain functions?? (HINT: In other languages, those functions are typically called "methods".)

For example, in Java, a class is a template that allows you to create (instantiate) objects. In turn, those objects can contain... basically anything - including functions (methods).

In JS, the class is just syntactic sugar. But what is it syntactic sugar for??? Some people assume it's syntactic sugar for an object. But that's not quite right. class is just syntactic sugar for a function - one that returns an object (which in turn can contain... basically anything).

In fact, if you wanna waste a night diving deep down a JS rabbit hole, try writing a function that will determine whether the argument passed in is a function... or a class. It's... damn-near impossible.

Why did it take me a while to truly appreciate this?? Well... it's very easy (maybe even natural) to look at any given function and assume that it returns some kinda scalar value - a string, a number, ...whatever. Of course, I always understood that a function could also return objects, but I was still thinking of those objects in terms of scalar values (i.e., an object - that contains strings, or an array - that contains numbers).

But it may not be immediately apparent to all programmers that, in JS, it's perfectly natural to have a function that:

  1. Holds other functions inside that function.
  2. Uses some of those functions only for its own internal workings ("private").
  3. Returns some of those functions to the caller ("public").

In other words, a JS function can pretty much perform all the operations that other devs, in other languages, call a class. And once you realize the power of JS's functions, you realize that JS's functions are classes - they're just classes in sheep's clothing.

To put this into a different context, I remember when jQuery first gained widespread adoption. And I'll be completely honest here - I thought it looked absolutely friggin foreign to me at the time. Because jQuery is basically an entire library that's built on the concept of anonymous functions. And at that point in my programming life, the idea of an anonymous function just didn't make any damn sense to me. (Like: "Why in the world would I want to write a function with NO NAME?? That I can't call again at some point in the future??? THIS FEESL LIKE INSANITY!!!")

Even once I got comfortable with jQuery's syntax, I didn't immediately appreciate the extent to which it was highlighting JS's use of functions as first-class citizens. Ironically, I didn't fully grasp some of jQuery's "lessons" until years after I'd stopped using it.


Alt Text

"Self-Aware" Functions

One of the key benefits of OOP objects is that they have a kinda "self-awareness" about them. No, I don't mean that they're gonna coordinate a counterstrike against humanity, leading to the Rise of the Machines. I mean that they can reveal things about themselves - and hide things about themselves.

But you don't need OOP to accomplish this. And this is where the Module Design Pattern shines.

Consider this example:

export class MyClass {
  externalVariable = 'foo';
  internalTrackingVariable = 'bar';

  doExternalProcessing() {
    // function to be called by the instantiator
  }

  doInternalProcessing() {
    // internal helper function
  }
}
const myInstance = new MyClass();
Enter fullscreen mode Exit fullscreen mode

In this example, internalTrackingVariable and doInternalProcessing() seem like prime candidates to be "private". But myInstance has full access to them. That's... not ideal.

[NOTE 1: There is a proposal - that's not yet finalized - to add private variables to JS classes. But since it's still just a proposal, I'm not gonna spend any more time thinking about it here.]

[NOTE 2: It's perfectly possible to create "private" variables/methods inside a JS class. It's called a closure. But the syntax required for it is not really... "intuitive" and it doesn't really feel like it's part of the class spec in any way.]

Now let's look at the same example with a Module Design Pattern:

export const MyModule = () => {
  const externalVariable = 'foo';
  const internalTrackingVariable = 'bar';

  const doExternalProcessing = () => {
    // function to be called by the instantiator
  }

  const doInternalProcessing = () => {
    // internal helper function
  }

  return {
    doExternalProcessing,
    externalVariable,
  }
}
const myInstance = MyModule();
Enter fullscreen mode Exit fullscreen mode

In the class example, everything is "public", unless we jump through some closure-hoops to ensure it's not so. But in the module example, everything is private, unless we specifically choose to expose it in the return object. Suddenly, controlling the "self-aware" aspects of the product object feels soooooo much easier.

IMHO, this is a huge argument in favor of the Module Design Pattern. If the MyModule caller tries to access myInstance.internalTrackingVariable they'll find that there is no internalTrackingVariable for them to reference.

This also has huge ramifications if we're trying to control the data integrity of our "class" members. Specifically, when I'm writing custom Hooks, I frequently employ this type of pattern:

export const useSomeHook = () => {
  const [counter, setCounter] = useState(0);

  const updateCounter = (newCounter = 0) => {
    // apply some validation to ensure that newCounter
    // can ONLY be a non-negative integer
    setCounter(newCounter);
  }

  return {
    counter,
    updateCounter,
  }
}
Enter fullscreen mode Exit fullscreen mode

See what I did there? I have a regular-ol' state variable. And I'm exposing the value in the return object. But I'm not exposing the natural setter. Instead, I'm only exposing my custom update function, which will first ensure we have a "proper" value before setting a new counter value.

Of course, you could just expose setCounter in the return object. But this would mean that the caller could set the value of counter to -5. Or 'zero'. Or 'chocolate strawberry banana'. By only exposing my custom updater function, I can then ensure that counter is only ever set to a "logical" value.


Alt Text

Shutting Up The Sycophants

I've laid out some practical reasons why the Module Design Pattern can be useful. But I'll be honest: There are also some personal reasons why I've really gravitated toward this pattern over the last year-or-so. Specifically, it's a fabulous way to shut up the dyed-in-the-wool, mindless, Functional Programming fanboys who can't help but fill their diaper any time they're affronted with that horrible no-good class keyword.

Years ago, JS-types would see me using a class and they'd cry that I should be mimicking all of their .prototype and closure techniques. And I'd think, "Yeah, umm... nope. Not gonna be doing that."

Then when I started getting heavy into React, some people would complain that I should be writing more functional components - even though functional components were (at the time) handicapped with far less functionality. And I'd think, "Yeah, umm... nope. Not gonna be doing that."

Recently, I've had some people around me even complain that a simple utility class was somehow "wrong" because I had the audacity to deploy the JS class keyword. And I'd think, "Yeah, umm... nope. Not gonna be doing that."

But recently, I've been converting many of my remaining classes over to a Module Design Pattern. Because the Module Design Pattern can honestly do all of the same things. And it does it in a way that's syntactically just as elegant as a class. And it does it in a way that undercuts all the whiny complaints of the FP fanboys. So with that in mind, it's just easier... to switch.

Consider this example:

I have a utility class that I've written about previously. It's a wrapper for localStorage. It looks something like this:

class Local {
  clear = () => {}

  getItem = itemName => {}

  removeItem = itemName => {}

  setItem = (itemName, itemValue) => {} 
}
export const local = new Local();
Enter fullscreen mode Exit fullscreen mode

Honestly??? I think this is a perfect use-case for a class. Nevertheless, I'd get the occasional comment from someone asking why I'd made it a horrible, no-good, unconscionable class. Some of those people even stated that it seemed like a logical use-case for a class!!! But that didn't stop them from wondering why I made it... a class!

To be frank, those kinda comments used to exasperate me. I mean, they'd basically admit that it made perfect sense as a class - and then they'd still whine about it being a class!!!

But at this point, I've basically solved this "problem". I recently turned this into an NPM package. And when I did, I used this syntax:

const Local = () => {
  const clear = () => {}

  const getItem = itemName => {}

  const removeItem = itemName => {}

  const setItem = (itemName, itemValue) => {}

  return {
    clear,
    getItem,
    removeItem,
    setItem,
  }
}
export const local = Local();
Enter fullscreen mode Exit fullscreen mode



 

There! Are you happy now???



 

Top comments (16)

Collapse
 
embrycode profile image
Mason Embry

This was an excellent article, thank you. It shines a light on something I’ve been struggling with (unknowingly) for years. I’ll try to keep this short. But first, one quick thing:

Is this the Module Design Pattern? I’m trying not to argue semantics but can’t you also export classes from modules? I think what we are talking about here is:

Factory Functions vs. Classes

Right? Both create objects. Both can do all the same things (inheritance, composition, encapsulation, polymorphism, etc.) The only real difference is performance optimization due to class creating a prototype and factory function creating a new scope for every instance, but this is a subtle difference until your JavaScript chops are more advanced. I’m curious your thoughts.

Anyway, the main point is this:

TLDR: Using factory functions instead of classes is not using FP. It’s still just OO.

Six years ago when I was learning to code, the interwebs was full of JS bros (of which I am one) screeching about how the class keyword was ruining JS. At the same time, I’m reading all kinds of articles about how OO sucks and we should all be doing FP. I thought this was the same argument. I thought we should not use class, but instead use functions only, and therefore we are doing FP instead of OO. Whoops.

So I don’t take any time to learn OO (because classes are bad and FP is better) but I’m definitely not doing FP because I’m writing React, state is all over the place, side effects are all over the place, etc. I’m basically just doing procedural programming and building big balls of spaghetti, refusing to learn anything about OO. I didn’t know what a constructor was until my third year as a programmer, making good money. So sad.

So, someone else I’m discussing this with just pointed out to me that whether we create our objects with classes or with factory functions is irrelevant. Either way, we are doing OO. And in fact I did watch a talk by Douglas Crockford where he said that in JS he does “class-free object oriented programming.” So, what I should have done all along is say, “well, there’s debate over how I should be creating my objects, so I’ll table that decision, but either way, I need to learn OO really well so I can learn to design complex systems.” I could have also said, “OO sucks so I’m going to write all my frontend code with Elm and do true FP.” But what happened instead was a bunch of confusion where I refused to learn OO but didn’t learn FP either and just spent 5 years afraid of classes and OO and stunting my growth.

I’m curious about your thoughts. Thanks so much for reading all that!

Collapse
 
bytebodger profile image
Adam Nathaniel Davis • Edited

Wow. Great feedback. I'll try to address some of these as succinctly as possible.

Is this the Module Design Pattern?

Umm... yes and no. It's hard to say, with absolute certainty, what is-or-is-not a "module design pattern" because any available definitions of the pattern are, from my research, incredibly vague. One example I could give you is simply the module.exports methodology that's commonly seen in Node. Using the pattern I've highlighted above is just a slightly updated version of module.exports. Of course, just because that package uses the terminology of "module" doesn't necessarily mean that it qualifies as the module design pattern, but as you've pointed out this all gets incredibly semantic very fast.

Factory Functions vs. Classes

YES. Exactly. In fact, that was one of the central themes of the second blog that I ever wrote on this site: dev.to/bytebodger/the-class-boogey...

TLDR: Using factory functions instead of classes is not using FP. It’s still just OO.

Yes. Yes, yes, YES. In fact, this article I wrote hits the same concept: dev.to/bytebodger/javascript-s-fun...

I’m curious about your thoughts.

You've pretty much hit the nail on the head. I'll summarize my thoughts (which basically mirror yours) like so:

  1. OOP can be a PITA. FP can be a PITA. Neither one is magically "better" or "worse". They are tools in your toolbox.

  2. That being said, very few people I've ever met actually practice FP. Hell, very few can even accurately define it.

  3. Most of the crap people talk about FP isn't even FP. FP doesn't mean "I write my code in JavaScript." It doesn't mean "I avoid using the class keyword." It doesn't mean "I write all of my code inside function() {...} or () => {...} statements."

  4. Although OOP understandably (and rightfully) gets a lot of crap for creating over-engineered, overly-abstracted concepts, it's still a crucial part of software engineering. And, as you've pointed out, it's a core part of JavaScript - whether people wanna admit that or not.

Collapse
 
embrycode profile image
Mason Embry

Amazing response, thank you. It feels good to be heard and understood, haha. Thanks for the therapy session. :-) I'll read those other articles!

Collapse
 
krisdover profile image
Kris Dover • Edited

Awesome article. Indeed classes are just self-aware modules with lots of syntactic sugar. But another important distinction I think is the support for mocking member functions when testing, e.g. with Jest you can jest.mock(Local) and it will mock the class so as to return instance mocks with mocked out member functions (methods). To my knowledge there is no built in support in Jest for mocking the module pattern (not to be confused with their support for mocking CommonJs/ES6 modules with jest.mock()), which is a bit of a shame. That said, it's not too hard to enumerate all own properties on the module object and set them to jest.fn() if they're functions.

Collapse
 
david03130 profile image
David Salcedo

There’s one thins that I’m still struggling to understand. I come from Java and C# and in those languages it’s common to have a class that extends from another and to overwrite properties and methods from the inherited class to customize functionality for each class. How do you extend in JavaScript with this approach, and how can you overwrite properties and methods that you have in the original “module”?

Collapse
 
bytebodger profile image
Adam Nathaniel Davis • Edited

Well, keep in mind that most JS/TS devs have a strong bias against inheritance. That being said, the answer to your question would be like so:

const Animal = () => {
  const breathesOxygen = true;
  const canPhotosynthesize = false;

  const breathe = () => 'breathed';

  const eat = () => 'ate';

  const sleep = () => 'slept';

  return {
    breathe,
    breathesOxygen,
    canPhotosynthesize,
    eat,
    sleep,
  }
}

const Mammal = () => {
  const hasHair = true;
  const givesBirthToLiveYoung = true;
  const hasFeathers = false;
  const isVenomous = false;
  const laysEggs = false;

  const nurseYoung = () => 'nursed';

  return {
    ...Animal(),
    hasHair,
    givesBirthToLiveYoung,
    hasFeathers,
    isVenomous,
    laysEggs,
    nurseYoung,
  }
}

const Platypus = () => {
  const isVenomous = true;
  const laysEggs = true;

  const injectVenom = () => 'injected';

  const nurseYoung = () => 'No!';

  const swim = () => 'swam';

  return {
    ...Mammal(),
    laysEggs,
    isVenomous,
    injectVenom,
    nurseYoung,
    swim,
  }
}

export default function App() {
  const platypus = Platypus();

  return (
    <div className="App">
      <div>The platypus</div>
      <div>breathes oxygen: {platypus.breathesOxygen ? 'yes' : 'no'}</div>
      <div>lays eggs: {platypus.laysEggs ? 'yes' : 'no'}</div>
      <div>injects venom: {platypus.isVenomous ? 'yes' : 'no'}</div>
      <div>&nbsp;</div>
      <div>Actions:</div>
      <div>Eat: {platypus.eat()}</div>
      <div>Nurse: {platypus.nurseYoung()}</div>
      <div>Sleep: {platypus.sleep()}</div>
      <div>Swim: {platypus.swim()}</div>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Which outputs this:

The platypus
breathes oxygen: yes
lays eggs: yes
injects venom: yes

Actions:
Eat: ate
Nurse: No!
Sleep: slept
Swim: swam
Enter fullscreen mode Exit fullscreen mode
Collapse
 
david03130 profile image
David Salcedo

Thank you, this makes sense! I'm fairly new to JavaScript and I started to do some research into the whole Module Design Pattern thing, and I am a bit lost. Do you know of any books or online resources that you would recommend to learn more about it?

Thread Thread
 
bytebodger profile image
Adam Nathaniel Davis

Unfortuntely, no. If you google "javascript module design pattern", you'll find other references. But I wasn't working off any primary resource.

Collapse
 
joshh71390 profile image
joshh71390

I know everyones different -> and it compiles to the same thing anyhoo, but I am actually A BIG fan of classes... Im finding this post in a mission to find out WHY people hate on classes, and if there is any actual downside, or if people are just saying phrases that they have heard other people say.

lol anyhow thanks for the detailed post it doesnt bring me to the end of my journey but it does bring me closer.

Collapse
 
spock123 profile image
Lars Rye Jeppesen

Modern javascript actually has real classes with both private, protected and public properties.

A few points I am not sure was covered:

  • using classes can be very elegant when extending functionality from a base class.

Yes this is also possible with importing the functions ,but much less elegant.

My point is that there's a use case for everything, and you seem to be 100% focused on React (frontend), and much less other frameworks, or backend.

Collapse
 
dillonheadley profile image
Dillon Headley

I thought class in js was basically syntax sugar. You can use the ‘new’ keyword without the ‘class’ keyword for similar effect.

Collapse
 
armando284 profile image
Armando

Great article, you have a really nice writing skill! Please keep it up!

Collapse
 
ecyrbe profile image
ecyrbe • Edited

Hi Adam,

Thanks for sharing your insights. I am not so found of the Module Design Pattern.

I think no-one should be opinionated about programming styles. Programming paradigms are tools, and as tools we should use the best one for the job when we see fit.

I like reminding my coworkers that javascript is in fact a prototype based object oriented programming langage with first class functions in it. It is not a pure functional langage like haskell, nor a pure object oriented langage like java used to be.

In Javascript we simply don't have the tools to do clean functionnal programming. And if doing something clean means using classes, you should do it. And in the same way, you should not use classes everywhere, not everything is a class.

Here are things that most functionnal programming langages offer and javascript don't :

  • expression statements (see tc-39 proposal for do-expressions) => alternative with IIFE
  • pattern matching (see tc39 proposal for pattern matching) => no clean alternative
  • pipeline operator ( see tc39 proposal for pipeline operator ) => no clean alternative
  • advanced monad tools (like do notation, etc, no proposal) => no clean alternative
  • standard native adt library => we need to use libraries like sanctuary, monet, etc
  • standard native algorithm library => we need to use libraries like ramda, lodash/fp, etc

So now, what is the issue everybody have with oop ?

From my point of view, the main issue with oop it's that the oop community abused two tools and made oop an over-engineering-fest (the enterprise syndrome):

  • using design patterns everywhere: singleton, factory, getter/setter boilerplate, abtracted classes
  • using polymorphism everywhere: making everything come with a hierarchy

On the opposite side, when maintaining big functionnal code bases, the main issues you face is that a lot of real objects (structure) are hidden by behaviour (functions).

React does not face this issue much, because components are the main building blocks of your infrastructure. So react components exposes structure naturally. And react hooks exposes behaviour naturally. And both gives you a nice and elegant balance that emerges naturally in your project (that's why react is awesome).

But in some part of your code that does not use react, identifying what your main building blocks are can be difficult and lead to bad designed functionnal code with functions-spagetti-fest. This is a big issue for maintenance. In some cases, using a class might be a good idea to make this code maintainable.

Now about why i don't like Module Design Pattern : No matter what some oop haters think, this is oop. Indeed, if what you are doing is, have an object instanciated by passing some parameters (it's obviously a constructor) and then calling functions to control the object (it's obviously a method), you are doing object oriented programming.

If some people really prefer writing boilerplate code to address the private field and method issue Javascript have right now... i would suggest them to use a babel transpiler and use tc39 private fields proposal. it's much more clean than using MDP.

Collapse
 
bytebodger profile image
Adam Nathaniel Davis

First, I wanted to thank you for taking the time to write out this thoughtful reply. And for the most part, I agree with you. But I can't really write too much more here that explains my thoughts on the matter because my responses to Maciej Sikora in this same comment section pretty much highlight why I've moved to this design pattern for the time being. Part of it, quite frankly, is just a matter of practicality on my part. (React is pretty-much already in this pattern - and I write a ton of React.) Part of it is exhaustion. (If I can write code in Style A or Style B, and I personally like both styles equally, then I might just veer toward the one that inspires less whining from the fanboys.)

But again, I agree with your points and I totally appreciate the thoughtful feedback!

Collapse
 
macsikora profile image
Pragmatic Maciej • Edited

Hey Adam, as always great writing. But as always I have some concern about your way of thinking :). I mean the "Module Design Pattern" is nothing more than everybody was doing before class keyword was around, and what has its origin in Scheme. What you call MDP is really an object constructor, it takes some arguments and bakes you an object which has properties and functions working with them, and because of closure has some private scope of access. And if it is stateful it is clear OOP, but OOP in the old JS way, the JS without classes.

I get your point in terms of fanboys hunting you, and because of them avoiding class keyword, but if they are not ok with a "class" but they are ok with stateful MDP, then most probably they don't understand what OOP is. Class and MDP are the same thing, using one over another is only implementation detail.

It is well-known that objects and closures over lexical scopes (LexicalClosures) are equivalent things; src

There is one reason more in the difference between such use of closure and class keyword, class creates prototype, which is shared for every object created by class constructor, whereas hand made constructor will create new scope for every created object. It can have a meaning if we create a lot of them.

Also take a look on this answer. Cheers Adam!

Collapse
 
bytebodger profile image
Adam Nathaniel Davis • Edited

Of course I totally agree with you. As a bit of further clarification/distillation, I'll summarize my recent turn toward MDP like this:

  1. React is my main focus (for now, at least - it tends to change every few years or so). Once React started moving much stronger to functions/Hooks, I found that nearly all of my React code looks much more like this pattern anyway. Maybe, when I'm writing a bunch of non-React stuff, I'll float back toward class. But for right now, it's easy to keep a consistent style across all my code.

  2. As outlined in the article, I do think there's one key benefit to this pattern over class - mainly, that it's somewhat "cleaner" and more intuitive to create private members. IMHO, that's not inconsequential. I find that many JS devs rarely even think about design patterns with regard to private variables, because the language doesn't make it easy for them to conceptualize them (like, for example, having a handy little private keyword available). So they rarely think about things like having protected access to update a variable. I've seen far too many instances where someone spins up a variable, alongside the variable's setter, and then they pass that setter around knowing dang well that the app will BREAK if that variable gets set to the "wrong" kind of value. Of course, this pattern doesn't fix that problem. But I do think it makes it easier to avoid it.

  3. Finally, this is absolutely a bit of a white flag on my part. The syntax I've shown above is something I'm perfectly comfortable with (again, because I'm a React dev). And as you've pointed out, the syntax above is truly just another version of what we get with class. But to be perfectly frank with you, if I can do something in two different ways, and I'm perfectly comfortable with both of those ways, but one of those ways will spawn an endless stream of petty squabbles from ill-informed language purists??? Well...