Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Using the Revealing Module Pattern in Javascript

The Revealing Module Pattern in Javascript

Zach Caceres

Javascript does not have the typical 'private' and 'public' specifiers of more traditional object oriented languages like C# or Java. However, you can achieve the same effect through the clever application of Javascript's function-level scoping. The Revealing Module pattern is a design pattern for Javascript applications that elegantly solves this problem.

The central principle of the Revealing Module pattern is that all functionality and variables should be hidden unless deliberately exposed.

Let's imagine we have a music application where a musicPlayer.js file handles much of our user's experience. We need to access some methods, but shouldn't be able to mess with other methods or variables.

Using Function Scope to Create Public and Private Methods

Let's first see how Javascript's function-level scope can help us create public and private methods.

We can move all functionality inside a function's scope. Then we return an object with the functions that we'd like accessible in other files.

// musicPlayerModule.js

var musicPlayer = function () {
  // Let's make sure no one can directly access our songList
  var songList = ['California Girls', 'California Dreaming', 'Hotel California'];  

  // We'll expose all these functions to the user
  function play () {
    console.log('Im playing the next song!');
  }

  function pause () {
    console.log('Im paused!');
  }

  function addTrackToMusicQueue (track) {
    songList.push(track);
    console.log('I added a song');
  }

  function showNextTrack () {
    console.log('My next track is', songList[0]);
  }

  // Let's hide this function
  function loadSong() {
    filesystem.loadNextSong();
  }

  return {
    playMusic: play,
    pauseMusic: pause,
    showNextTrack: showNextTrack,
    addTrack: addTrackToMusicQueue
  }
}

const musicModule = musicPlayer(); // invoke our musicPlayer to return it's object (module)
musicModule.playMusic(); // 'Im playing the next song!'
musicModule.pauseMusic(); // 'I'm paused!'
musicModule.showNextTrack(); // 'The next track is California Girls'

// Things we can't access...
musicModule.loadSong(); // error: not a function
musicModule.songList.push('White Rabbit'); // undefined error

Now we can access all the methods that we need on our musicModule object. But we can't fiddle with our songList or access the loadSong() method. These are both private.

This works fine. But there's one big problem.

We're using musicPlayer as a namespace to hold our functions. But wait, our musicPlayer is a function that's still exposed to the global scope!

Someone could come along and invoke it again, creating a new musicPlayer. Then we would have multiple instances of musicPlayer floating around, polluting our environment and causing all sorts of confusion.

Hiding Your Module with an IIFE

The best way to avoid exposing your top-level function to the global scope is to wrap everything in an IFFE. An IIFE is an immediately-invoked function expression. That's a mouthful. It just means that we call (invoke) this function as soon as the file is run (immediately).

Since our function has no name, we call it an expression. Since it has no name, it can never be invoked elsewhere.

Here's how that looks:

var musicModule = (function () {
  // Let's make sure no one can directly access our songList
  var songList = ['California Girls', 'California Dreaming', 'Hotel California'];  

  // We'll expose all these functions to the user
  function play () {
    console.log('Im playing the next song!');
  }

  function pause () {
    console.log('Im paused!');
  }

  function addTrackToMusicQueue (track) {
    songList.push(track);
    console.log('I added a song');
  }

  function showNextTrack () {
    console.log('My next track is', songList[0]);
  }

  // Let's hide this function
  function loadSong() {
    filesystem.loadNextSong();
  }

  return {
    playMusic: play,
    pauseMusic: pause,
    showUpNext: showNextTrack,
    addTrack: addTrackToMusicQueue
  }
})(); // our IIFE function (surrounded with parens) is invoked here

musicModule.playMusic(); // 'Im playing the next song!'
musicModule.pauseMusic(); // 'I'm paused!'
musicModule.showUpNext(); // 'The next track is California Girls'
musicModule.loadSong(); // error: not a function
musicModule.songList.push('White Rabbit'); // undefined

Our function-level scope still keeps our methods and variables public and private based on whether we expose them in the return object.

But this time, we avoid the risk that we can invoke our module elsewhere in the code.

In other files we can now use musicModule's' functionality – a well-encapsulated module in our global scope!

@victorighalo
Copy link

victorighalo commented Sep 21, 2019

This is a Very lucid explanation. I am just learning about this pattern. Thanks.

@tranhducvnn
Copy link

tranhducvnn commented Oct 2, 2019

Great explanation. I have learned new knowledge from this. Thanks.

@Calebyang93
Copy link

Calebyang93 commented Nov 5, 2019

Great explanation. Thanks for sharng! learnt a lot.

@jetjokers24895
Copy link

jetjokers24895 commented Jan 9, 2020

Thanks you very much!. This is very good for me!

@nakulvijay56
Copy link

nakulvijay56 commented Feb 25, 2020

Nice explanation, but can someone give the advantage and the disadvantage of using this design pattern. I am just learning this pattern.

@chirag97
Copy link

chirag97 commented Apr 7, 2020

thanks for the subtle and to the point explaination.

@Hoxtygen
Copy link

Hoxtygen commented Apr 17, 2020

Thanks for this piece @zcaceres
Is this a typo The best way to avoid exposing your top-level function to the global scope is to wrap everything in an **IFFE**

@NelzkieCoder
Copy link

NelzkieCoder commented Jun 29, 2020

Thanks

@isam123
Copy link

isam123 commented Jul 2, 2020

Thanks Man

@akihshen
Copy link

akihshen commented Jul 5, 2020

Thanks

@jdiegosierra
Copy link

jdiegosierra commented Aug 21, 2020

I don't understand the advantage of using Hiding Your Module with an IIFE. Can someone show me a code example with the problem?

@omundy
Copy link

omundy commented Sep 17, 2020

Where would you place "use strict"; if using several modules? Inside or outside?

@tiomno
Copy link

tiomno commented Oct 29, 2020

Thanks, neat explanation!

@jdiegosierra, I was playing with this pattern and came up with a rather contrived example, but you can get the idea. Check out the comments. ;)

Revealing Module Pattern without IIFE

Revealing Module Pattern with IIFE

Hope it helps!

@allpro
Copy link

allpro commented Mar 20, 2021

I've used this pattern a looong time, but rarely as a single instance (IIFE). To me it's greatest power is as an instantiable module; each instance having its own 'configuration' and 'state'. It's like a React hook or similar component that provides shared-logic. For me this is a much more common need than a singleton.

Every time you call a revealing module, like const foo = ModuleFunction(config), it's the same as calling const foo = new ModuleFunction(config). The args you pass become the 'configuration' of that specific instance, and you can create as many instances as desired. This is a powerful and convenient way to reuse logic.

For example, I have a FormManager module that creates a form-specific object to manage every aspect of a form's data, including custom validation and caching. I can create as many form-manager instances as needed without worrying about 'bleed' from one instance to another.

A singleton module (IIFE) is useful only in cases where there can be exactly one of something.

@niksolaz
Copy link

niksolaz commented May 27, 2021

thanks for explanation. this is very cool

@iampsjain
Copy link

iampsjain commented Jun 3, 2021

thanks for explanation. this is very cool

@5hraddha
Copy link

5hraddha commented Sep 24, 2021

Thanks for the short and to the point explanation of 'Revealing Module Pattern'. I am almost a beginner and learning how to use Web pack as a bundler. But, then I was trying to understand why do we need a bundler like Webpack in the first place. This led me to research how JS works in browsers, the problems we get with scoping and how IIFEs helped in resolving the problem to an extent using 'Revealing Module Pattern'. That is how I got to read your article. It surely helped me. Thanks again!!

@pp-id
Copy link

pp-id commented Nov 13, 2021

Thanks it was actually helpful

@Youssef9798
Copy link

Youssef9798 commented Dec 25, 2021

Thanks, it was very smooth and easy words!

@ikram-patah
Copy link

ikram-patah commented Dec 25, 2021

👍
Really good and concise explanation. I think worth explaining that the using the IIFE assignment is a singleton pattern, which takes away the ability to create multiple instances of the module.

@seb-thomas
Copy link

seb-thomas commented Jan 13, 2022

Might be useful to show how to pass args (unless i missed it in my quick look :D )

...
  return {
    funky: (arg)=> console.log(arg),
  };
};

module.funky("Sup");

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment