DEV Community

Cover image for The Revealing Module Pattern | Javascript
nickap
nickap

Posted on • Updated on

The Revealing Module Pattern | Javascript

One of the biggest challenges of every developer is to maintain code organization.
There is a plethora of the so called "best practices" someone can follow to result in a better code, and also, to become a better programmer.
The revealing module pattern is one of those best practices someone can use to achieve better code organization.

The Revealing Module Pattern in Javascript

What is the Revealing Module Pattern?

In JavaScript, there are no private and public specifiers.
But as you may know, you can achieve the private effect with closures.

Nice opportunity to remember that thing... closures!

Having a function (let's call it funcInner) encapsulated inside another function (let's call it funcOuter), and returning the funcInner, then, we actually have private "staff" declared inside the funcOuter that only live inside these functions. In both of them.

When funcOuter is executed, it creates a new scope for itself. This scope contains its variables, parameters, and inner functions. When funcInner is defined inside funcOuter, it has access to all the variables and parameters in the scope of funcOuter, including those that are not passed as arguments to funcInner.

Code Example:

function funcOuter() {
  const outerVariable = "I am outer!";

  function funcInner() {
    const innerVariable = "I am inner!";
    console.log(outerVariable); // prints "I am outer!"
    console.log(innerVariable); // prints "I am inner!"
  }

  return funcInner;
}

const funcInner = outerFunction();
funcInner(); // prints "I am outer!" and "I am inner!"


Enter fullscreen mode Exit fullscreen mode

Let's go one step further.

So, the pattern:
Using closures, we can actually create modules with private and public methods and variables.
This is what we do in the revealing module pattern.
We use closures to create private methods and variables that are hidden from the outside world. We return an object with what we specify as public.

This pattern allows us to encapsulate functionality and create modular, reusable code.

Let's see it in action

const myModule = (function () {
  let privateVar = "Hello outer world";

  function privateFunction() {
    console.log(privateVar);
  }

  function publicFunction() {
    privateFunction();
  }

  return {
    publicFunction: publicFunction,
  };
})();

myModule.publicFunction(); // Outputs "Hello outer world"
Enter fullscreen mode Exit fullscreen mode

To demonstrate the pattern, we created our module using an immediately invoked function expression (IIFE).
The publicFunction is returned as an object from the closure and can be accessed outside the closure.

Of course, there is no need to use IIFE.
Let's see a...

...A real life example

While I was creating a Vue composable (like hooks for react), I wanted to attach some eventListeners in a specific occasion while also i would like to remove them whenever I like.
This sounds like a perfect moment to use the revealing module pattern
You can see here how I return only the methods that are responsible only to add and remove the event listeners.

export function touchDeviceHandler(options, callback){
  // Private Variables
  let startY = 0;
  let scrolling = false;

  //Private Methods
  const onTouchStart = (event: TouchEvent) => {
    console.log("onTouchMove");
  };

  const onTouchMove = (event: TouchEvent) => {
    console.log("onTouchMove");
  };

  const onTouchEnd = () => {
    scrolling = false;
  };

  // We are gonna expose these two
  const addTouchListeners = () => {
    document.addEventListener('touchstart', onTouchStart);
    document.addEventListener('touchmove', onTouchMove);
    document.addEventListener('touchend', onTouchEnd);
  };

  const removeTouchDeviceListeners = () => {
    document.removeEventListener('touchstart', onTouchStart);
    document.removeEventListener('touchmove', onTouchMove);
    document.removeEventListener('touchend', onTouchEnd);
  };
/* Returned Object, containing addTouchListeners and removeTouchDeviceListeners functions*/
  return {
    addTouchListeners,
    removeTouchDeviceListeners
  };
}
Enter fullscreen mode Exit fullscreen mode

And here you can see how I use touchDeviceHandler.

I just destructure the returned object

const { addTouchListeners, removeTouchDeviceListeners } = touchDeviceHandler(
    options,
    fire
  );
Enter fullscreen mode Exit fullscreen mode

And eventually, lower in the code, it's so clean and easy for someone to understand what I am actually doing when calling

removeTouchDeviceListeners();
Enter fullscreen mode Exit fullscreen mode

In case you are interested, here is the repo

But...

I could just use closures to expose either the code responsible to add listeners, or the code to remove listeners, and handle the situation in another way.
But hey, we want the cleaner way!
Why to bother my future self, or another coder, with something like touchDeviceHandler()(). This would be responsible to run the returned function if I was just using closures. And what this code actually doing? It removes the listeners? ...or maybe add them? or...I don't remember, ooh I have to see the responsible function...

Eventually

Using the revealing module pattern, the code is so clean and modular that I can even expose addTouchEventListeners(); and removeTouchDeviceListeners(); from the composable. That way, the user of the composable could act on his own style, adding and removing the listeners whenever he likes.

Finally

Advantages of the Revealing Module Pattern

  • Encapsulation.
  • Preventing Variable Name Collisions.
  • Modularity.

Thank you for reading up to this line!

Top comments (2)

Collapse
 
xmohammedawad profile image
Mohammed Awad

great

Collapse
 
nickap profile image
nickap

Glad you liked it @muhmmadawd !