DEV Community

Cover image for Revealing Module Pattern in Javascript
Patricia Nicole Opetina
Patricia Nicole Opetina

Posted on • Edited on

Revealing Module Pattern in Javascript

[JS #2 WIL Post]

Encapsulation is not inherent in Javascript. There are no modifiers like private and public to help protect objects from unwanted access. This is one reason why function scope is important in JS. Each function creates a new scope. The scope dictates the visibility of the variables inside a function.

For example in the snippet below, the gridItems attribute of the gameboard cannot be accessed directly unless the getter/setter method is used. Thus, the possibility of setting incorrect grid item values will be removed.

Note that the checking of row, column, diagonal winners of the game board is not accessible from the objects that will be importing it. Only the returned functions of GameBoard will be accessible outside it.

const GameBoard = (function() {
//no one can access this variable directly
    let gridItems = Array(9).fill('I');

    function resetBoard() {
        gridItems = Array(9).fill('I');
    }

    function setGridItemValue(index) {
        gridItems[index] = turn;
    }

    function getGridItems() {
        return gridItems.slice(0);
    }

    function checkWinner(board) {
        if(checkDraw(board)) {
            return "tie";
        }

        if(checkRowWin(board) || 
           checkColumnWin(board) || 
           checkDiagonalWin(board)) {
            return winner;
        }

        return null;
    }

    ...

    return {
        setGridItemValue,
        checkWinner,
        resetBoard,
        getGridItems,
    }
}());
Enter fullscreen mode Exit fullscreen mode

One more important thing to note about this pattern aside from encapsulation is its use of IIFE (immediately invoked function expressions). It is run as soon as it is defined. It is sometimes called a self-executing anonymous function and has two parts:
A. The function itself with the Grouping operator

(function () {
  statements
})
Enter fullscreen mode Exit fullscreen mode

B. The second one creates the IIFE () through which the JS engine will directly interpret the function.

The second part makes sure that there is no accidental invocation of the function, thus creating a lot of GameBoard objects. To use the GameBoard, the user can just call the publicly available functions, i.e,

GameBoard.resetBoard();
GameBoard.checkWinner();
Enter fullscreen mode Exit fullscreen mode

Now, the GameBoard module can be used in other parts of the JS projects. Check the usage of the GameBoard function in this repository.

References
[1] Revealing Module Pattern
[2] IIFE

Top comments (4)

Collapse
 
peerreynders profile image
peerreynders • Edited

Just thinking out aloud ...

I was wondering what the difference between the module pattern and the revealing module pattern was.

Addy Osmani documented both in 2012 attributing the module pattern to Richard Cornford (2003) and the revealing module pattern to Chris Heilmann (2007).

Using an IIFE to create a closure that the object methods could use to share private data was already part of the "classic" module pattern:

// Classic Module Pattern
const counter = (function () {
  // private data
  let count = 0;

  return {
    increment() {
      count += 1;
    },
    twice() {
      // PROBLEM
      counter.increment();
      counter.increment();
    },
    current() {
      return count;
    },
  };
})();

console.assert(counter.current() === 0);
counter.increment();
console.assert(counter.current() === 1);
counter.twice();
console.assert(counter.current() === 3);
Enter fullscreen mode Exit fullscreen mode

The problem with the classic module pattern is that you have to use the outside variable (here counter) to reference a public method within any other object method.

One way to get around this is to use this:

// Classic Module Pattern
// with `this`
const counter = (function () {
  let count = 0;

  return {
    increment() {
      count += 1;
    },
    twice() {
      // quick fix: use `this`
      this.increment();
      this.increment();
    },
    current() {
      return count;
    },
  };
})();
Enter fullscreen mode Exit fullscreen mode

But around this time people were trying to avoid this as much as possible. So the revealing module pattern gets rid of intermediate references through the object by only using standalone functions that reference one another or shared data directly within the closure. Only those functions that are to be revealed to the public are attached as function values to the object literal that is returned.

// Revealing Module Pattern
// standalone functions - not object methods
const counter = (function () {
  let count = 0;

  return {
    increment,
    twice,
    current,
  };

  // function declarations
  // hoist automatically to the top
  function increment() {
    count += 1;
  }

  function twice() {
    // Just use function directly
    increment();
    increment();
  }

  function current() {
    return count;
  }
})();
Enter fullscreen mode Exit fullscreen mode

So the revealing module pattern isn't returning an object in the conventional sense but a "map" of non-method function values that access one another and shared data through the closure they were created in.

Just in case anybody was curious.

Collapse
 
pat_the99 profile image
Patricia Nicole Opetina

Wow thank you for this peerreynders!!

Collapse
 
peerreynders profile image
peerreynders

... come to think of it, as there is no reliance on this, the return value doesn't have to be an object anymore.

const initialValue = 0;
const valueTuple = (function (init) {
  let val = init;

  const getValue = () => val;
  const setValue = (newVal) => {
    val = newVal;
  };

  return [getValue, setValue];
})(initialValue);

const [current, set] = valueTuple;
console.assert(current() === initialValue, 'initialization failed');
set(1);
console.assert(current() === 1, 'set to 1 failed');
set(3);
console.assert(current() === 3, 'set to 3 failed');
Enter fullscreen mode Exit fullscreen mode

Though perhaps now we aren't revealing a "module" anymore.

Collapse
 
mayankav profile image
mayankav

definitely a good read Patricia :)