DEV Community

loading...
Cover image for Modules vs Classes - How to Manage Privacy in JS

Modules vs Classes - How to Manage Privacy in JS

Ilê Caian
I like dogs and music. Eventually I code. 🐶woof!
・5 min read

The Object-Oriented Paradigm made a huge shake in the way developers think and write code even if you don't like it or don't like its premises. This not so new paradigm of encapsulating data and procedures in form of attributes and methods influenced a lot of recent languages even if some of them are not using it as its main paradigm.

Languages like: C++, Java, Python, and even JavaScript are considered and declared languages that implement the Object-Oriented Paradigm (OOP). As will be discussed, JavaScript has its own way of dealing with Objects with some specificities. But first, there is a starting point that should be discussed: there's one essential fact in JavaScript that goes in the wrong direction of OOP: non-existence of encapsulation.

There's a repo with some tests too! Check it out!

Classes and Objects

Classes are definitions of data-types: what data they will store/hide and how it should behave. An instance of one Class will be able to execute functions as methods and store data as attributes. Those instances are the so-called Objects that lives inside the runtime execution of a program.

Photo by Dan Dimmock on Unsplash

Photo by Dan Dimmock on Unsplash

One of the important features in OOP is the fact that the Objects should have the ability to encapsulate(hide) its data. That means that if someone tries accessing some information from the Object, it should not be able to do it if the Class says so. Consider the following example:

Let's say that Elon Musk 🧑‍💼 created an awesome Trash Can that can perform 3 simple tasks:

  • Throw one 'junk' item into the Trash Can
  • Clean all items inside the Trash Can, all at once
  • One button that shows at the display if the Trash Can is fully empty or not

The interface for that TrashCan is something like:

TrashCan {
  throwAway(item);
  clean();
  isEmpty();
}
Enter fullscreen mode Exit fullscreen mode

As JavaScript has the class keyword, it's possible to consider one implementation of this Trash as the following

class TrashCan {
  constructor() {
    this.items = [];
  }

  throwAway(item) {
    this.items = [...this.items, item];
  }

  clean() {
    this.items = [];
  }

  isEmpty() {
    return this.items.length === 0;
  }
}

var elonTrashCan = new TrashCan();
Enter fullscreen mode Exit fullscreen mode

Now the elonTrashCan is empty and ready to start doing its work. But what happens with the execution of

elonTrashCan.throwAway('paper ball');
elonTrashCan.throwAway('empty Starbucks cup of coffee');
elonTrashCan.throwAway('empty package of Cookies');

elonTrashCan.clean();

elonTrashCan.items = ['SpaceX secret project'];

console.log(elonTrashCan.isEmpty()); // --> ???
Enter fullscreen mode Exit fullscreen mode
  1. Elon Musk 🧑‍💼 will be mad at us that we broke his Trash Can
  2. elonTrashCan.isEmpty() will return false, because we defined elonTrashCan.items with 1 item inside
  3. elonTrashCan.items is not accessible, so the elonTrashCan.isEmpty() call will returns true

The answer is option 2. It is possible to access items inside the Object instance even without explicit items declaration outside constructor.

Using that example and considering an ideal Object-Oriented language implementation, the execution of elonTrashCan.items should result in an error of the program trying to access a private attribute. In JavaScript, these calls are possible, valid ones, and results in no error.

So, isn't it possible to create Privacy in JavaScript? Is there a way of hiding data from outside the Object and exposing just public data?

Photo by Folco Masi on Unsplash

Photo by Folco Masi on Unsplash

Module Pattern

The good news is there's one behavior in JavaScript that provides something related to privacy: Closures. There's this post written about Closures in case of interest

Using Closures for hiding variables and functions is a good approach for encapsulate data inside one instance and just expose the desired interface of it.

But how do actually this works?

Let's create the same Elon Musk 🧑‍💼 TrashCan Object writing a Function and returning just the public interface of it as the following code

const TrashCan = () => {
  let items = [];

  const throwAway = item => {
    items = [...items, item];
  }

  const clean = () => {
    items = [];
  }

  const isEmpty = () => {
    return items.length === 0;
  }

  return {
    throwAway,
    clean,
    isEmpty,
  }
}

var elonTrashCan = TrashCan();
Enter fullscreen mode Exit fullscreen mode

And for the new elonTrashCan let's try to execute the same code as above

elonTrashCan.throwAway('paper ball');
elonTrashCan.throwAway('empty Starbucks cup of coffee');
elonTrashCan.throwAway('empty package of Cookies');

elonTrashCan.clean();

elonTrashCan.items = ['SpaceX secret project'];

console.log(elonTrashCan.isEmpty()); // --> ???
Enter fullscreen mode Exit fullscreen mode
  1. Elon Musk 🧑‍💼 will be even madder at us that we broke his second Trash Can
  2. elonTrashCan.isEmpty() will return false, because we again defined elonTrashCan.items with 1 item inside
  3. elonTrashCan.items is not accessible, so the elonTrashCan.isEmpty() call will returns true

Actually something very strange happens:

  • elonTrashCan.isEmpty() returns false because our internal items are empty
  • elonTrashCan.items has 1 item in it

Photo by Scott Gummerson on Unsplash

Photo by Scott Gummerson on Unsplash

Using this approach, it is possible to 'limit' the outside world on accessing just the desired interface and has hidden content inside it. On the other hand, JavaScript lets the definition of new properties in runtime execution, even with the same names used by its Closure.

The code inside of the Closure will not depend on those new properties, as the original ones are stored inside that Closure, unaccessible. And, the original mission is now accomplished: Privacy. The Module Pattern is viable for attributes and can be used for hiding methods too.

For the side-effect with the creation of new properties, it's hardly suggested not to change the original interface and even make some tests before using those properties like

if(typeof elonTrashCan.items === 'undefined') {
  console.log('No exposed items!') // --> No exposed items!
}
Enter fullscreen mode Exit fullscreen mode

Wrap up

After some discussion about the Object-Oriented Paradigm and the Class implementation of JavaScript, maybe the JS Class is not the best choice for creating Objects with private data if you are not using a transpiler like Babel.

Using Closures and the Module Pattern it is possible to accomplish Privacy in JavaScript in a simple and reusable way. If the class implementation is inevitable, consider using a transpiler or a more robust approach with some use of the Module Pattern. The use of transpilers is hardly incouraged!

Even with some apparent losses like inheritance, there are still valid ways of implementing that benefits even with Modules.

Did I miss something? Is there a thing that you think it's not clear? Feel free to reach me at the comment section or by message and discuss it!

References

Discussion (5)

Collapse
trippytrippy profile image
trippy-trippy

"Actually something very strange happens:

elonTrashCan.isEmpty() returns false because our internal items are empty"

Is it me or elonTrashCan.isEmpty() returns true?

Collapse
caiangums profile image
Ilê Caian Author

Hey! Thanks for your reply!

Actually, elonTrashCan.isEmpty() returns false as TrashCan is a Module and not a Class. You can read more about it at the related and cited post about Closures

You could also run the test code and see it running.

Collapse
saradari profile image
saradari

It's not you :), I think it's just an error, should be:
"3 - elonTrashCan.items is accessible, so the elonTrashCan.isEmpty() call will returns FALSE"

Class properties are public by default. developer.mozilla.org/en-US/docs/W...
Closure doesn't expose 'items', but class does and this is the big difference explained in the article.

Collapse
caiangums profile image
Ilê Caian Author

As I mentioned in another reply, I'm not actually using Classes. What you said is correct for Class 😄

Collapse
karranb profile image
Karran Besen

🔝