DEV Community

Cover image for The Revealing Module Pattern | Javascript
nickap
nickap

Posted on • Edited 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 better code.

The revealing module pattern is one of those practices someone can use to achieve better code organization. And it's a pattern that modern features of frontend frameworks are based on. Like React Hooks and Vue Composables.

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 closures!

A simple example: Let's say we have a function funcInner encapsulated inside another function funcOuter alongside some more variables and even methods. Then, if we return only the funcInner, we eventually have private staff declared inside the funcOuter that only live in the scope of this method.

When funcOuter is executed, it creates a new scope for itself. This scope contains its variables, parameters, and inner functions. When funcInner is defined, 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!";
  const thisIsInsideFuncOuterScope = "Can't access from outside"

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

  return funcInner;
}

const func = funcOuter();
func(); 
// prints "I am outer!" and "I am inner!"
// We don't have access on `thisIsInsideFuncOuterScope`
// We could also run `funcOutter()()` to immediately run the returned function

Enter fullscreen mode Exit fullscreen mode

The next step:

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 methods and variables that are hidden from the outside world. All these exist inside a function and we only return an object that contains only what we want to expose.

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

Let's see it in action

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

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

  function publicFunction() {
    privateFunction();
  }

  return {
    publicFunction: publicFunction,
  };
};

const { publicFunction } = myModule(); 
publicFunction() // Outputs "Hello outer world"
Enter fullscreen mode Exit fullscreen mode

The publicFunction is returned as an object from the closure and can be accessed outside the closure.

...A real life example

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 will expose this
  const addTouchListeners = () => {
    document.addEventListener('touchstart', onTouchStart);
    document.addEventListener('touchmove', onTouchMove);
    document.addEventListener('touchend', onTouchEnd);
  };

  // We will expose this too
  const removeTouchDeviceListeners = () => {
    document.removeEventListener('touchstart', onTouchStart);
    document.removeEventListener('touchmove', onTouchMove);
    document.removeEventListener('touchend', onTouchEnd);
  };
/* Returned Object, containing only what we want to expose */
  return {
    addTouchListeners,
    removeTouchDeviceListeners
  };
}
Enter fullscreen mode Exit fullscreen mode

And here you can see how we 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

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 at his convinience.

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 !