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!"
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"
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
};
}
And here you can see how I use touchDeviceHandler
.
I just destructure the returned object
const { addTouchListeners, removeTouchDeviceListeners } = touchDeviceHandler(
options,
fire
);
And eventually, lower in the code, it's so clean and easy for someone to understand what I am actually doing when calling
removeTouchDeviceListeners();
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)
great
Glad you liked it @muhmmadawd !