Skip to content

Instantly share code, notes, and snippets.

@ian-schu
Last active April 28, 2024 19:20
Show Gist options
  • Save ian-schu/8e768a27fdfc4f7197af31fbca3fa8d7 to your computer and use it in GitHub Desktop.
Save ian-schu/8e768a27fdfc4f7197af31fbca3fa8d7 to your computer and use it in GitHub Desktop.
Quick intro to JS 'Module' design pattern using IIFEs

Quick intro to the Module design pattern

(with some help from Immediately Invoked Function Expressions)

The goal is to create an object where only SOME of the properties and methods are public (able to be accessed by other parts of the program) and the rest of the properties and methods are private (aka "internal", cannot be accessed from outside).

Psuedocode Module Example

Here's the general idea:

person = {
	private properties: {
		stomach,
		brain,
		heart
	}
	Private methods: {
		circulateBlood()
		digestFood()
	}

	public properties and methods: {
		mouth,
		ears,
		hands,
		feet,
		nose,
		eyes,
		see()
		eat(),
		speak(),
		listen(),
		smell()
	}
}

^^ In this case, invoking person.see() should do something, but we should not be able to call person.digestFood() or set the value of person.stomach.

How do we do this?

Let's start with how to make an IIFE:

We have to use something called an IIFE (Immediately Invoked Function Expression). This is basically a hack where we force the JavaScript engine to immediately run an anonymous function, and return it. Because the function is anonymous it cannot be called again; and because it is returned immediately, only what is inside the return block can be accessed by the rest of the program.

Start with an anonymous function:

function () {
  let x = 2
  return x*2
}
// Returns 4

The way to make this into an IIFE is:

  • Write a generic invocation statement like myFunction()
  • Wrap that function name in parentheses like so (myFunction)()
  • Paste over the inside of those parentheses with your anonymous function definition.

The end result should look something like this:

(function() {
	let x = 2;
	return x * 2;
})();
// Returns 4 *immediately*

^^ This is a super-basic IIFE. When the JavaScript interpreter sees this, it will invoke the function immediately and execute the return statement.

This won't do much for us yet, but notice that you can assign a variable equal to this IIFE:

let newVariable = (function() {
	let x = 2;
	return x * 2;
})();
// ^^ This assigns a value of 4 to newVariable

Now we're getting somewhere.

Now we can create a module with an IIFE

Here's how you would do it:

let myModule = (function () {
  // PRIVATE section of the module (above the return block)
  // You can set up any internal functions and variables you like:
  // Create a private number with value between 0 and 5:
  let privateNum = Math.random()*5;

  // Create a private function that squares the number:
  function () squarePrivate{
    privateNum *= privateNum;
  }

  // Run the square function:
  squarePrivate();

  // PUBLIC section of the module is everything inside the RETURN OBJECT:
  return {
    // A method that returns the value of the private number
    whatIsTheNum: function(){
      return privateNum;
    },
    // A method that subtracts 2 from the private number
    reduceTheNum: function(){
      privateNum -= 2;
    }

  }
})()

What happens when the interpreter sees this?

  1. Runs your anonymous function.

  2. Sets myModule equal to everything inside the IIFE's return statement.

What can you do after that?

  1. You can access all public properties and methods of myModule. For instance, you can invoke myModule.whatIsTheNum() and it will return the internal value of privateNum.

  2. You cannot directly call the module's private methods. E.g. you cannot invoke myModule.squarePrivate() <--- that will return undefined and do nothing.

  3. You also cannot directly access the module's private properties, meaning you cannot access myModule.privateNum. Again, that will return undefined.

Some other important notes

Notice a couple other things about the example above:

  1. It is entirely up to the coder whether private elements are accessible, or how. In this case we allowed whatIsTheNum to return the value of a private property, but that is certainly not required. You can lock it up as tightly as you want.

  2. Notice that our public methods are using the magic of closures to interact with the private properties inside the module. What is this magic, again? The magic part is that our public methods are able to interact with private variables inside an anonymous function which already finished running! That's fuckin weird! But closures allow us to do that. When those public methods are returned, they close over all the private internals that were available at the time the IIFE executed. Because of that, we can still interact with those 'internals' later.

Further reading:

JavaScript Design Patterns (Addy Osmani) The Module Pattern https://addyosmani.com/resources/essentialjsdesignpatterns/book/#modulepatternjavascript

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