• Programming
  • JavaScript

Applications of Immediately-Invoked Function Expressions

October 25, 2020 • 2 min read

Avoiding global variables and the Module Pattern.

In the last post we learned what Immediately-Invoked Function Expressions (IIFE) are, and in this post I will touch on their use cases and provide examples to go along.

Avoiding Global Variables

You want to avoid global variables in your programs since they can be harder to debug, make your code harder to test, and their values can easily be changed unintentionally.

IIFEs are one way to avoid the need for global variables. This is because they can be used to create closures. A closure consists of a function and the variables and parameters within the scope of that function.

Therefore, if we have a function defined inside another function, the inner function will have access to the variables and the parameters of the outer function. As a result, instead of having global variables to store our values, we can use IIFEs to create closures.

That was a lot, but we can clarify it with an example.

Using IIFE instead of global variables

Imagine we want to create a simple function to keep track of points in a game. One way to do this is to have a global variable that gets updated whenever this function is called.

That can look like this.

1 let totalPoints = 0
3 function countPoints() {
4 totalPoints += 1
5 return totalPoints
6 }
8 // After each call the totalPoints gets updated by 1,
9 // doing what we expected it to.
10 console.log(countPoints()) // -> 1
11 console.log(countPoints()) // -> 2
12 console.log(countPoints()) // -> 3

The issue is that the totalPoints variable is global which could result in issues mentioned previously. We can change our implementation to use an IIFE to better keep track of the total points.

1 // By Immediately invoking the function expression, a new function is returned.
2 // The returned function is stored in the variable "incrementPoints" and it can be
3 // called like any other function.
4 const incrementPoints = (function(pointsEarned) {
5 let totalPoints = 0
7 return function () {
8 totalPoints += pointsEarned
9 return totalPoints
10 }
11 })(10)
13 // After each call, the totalPoints gets updated by 10, the value of the
14 // pointsEarned parameter.
15 console.log(incrementPoints()) // -> 10
16 console.log(incrementPoints()) // -> 20
17 console.log(incrementPoints()) // -> 30

In this code snippet, the totalPoints is defined inside the body of the IIFE, the outer function, instead of in the global scope. In order to increment the points, we return a new function. This is the inner function which has access to the totalPoints variable and can increment it by the value of pointsEarned.

Module Pattern

IIFE's are also useful for implementing the module pattern in JavaScript. This programming pattern allows us to group related functions and variables within the same scope, making our code more organized, easier to debug, and easier to test.

To implement the module pattern, instead of returning a function from our IIFE, we will return an object. Let's expand our example and implement it using the module pattern.

Currently we only support incrementPoints, but we now want to add decrementPoints and addBonusPoints. We want all of this to be in the same scope and in a module we can export and import when needed.

1 const PointsIncrementer = (function() {
2 let totalPoints = 0
4 return {
5 incrementPoints: function() {
6 totalPoints += 1;
7 },
8 decrementPoints: function() {
9 totalPoints -= 1;
10 },
11 addBonusPoints: function (bonusAmount) {
12 totalPoints += bonusAmount
13 },
14 getPoints: function () {
15 return totalPoints
16 }
17 }
18 })()
20 console.log(PointsIncrementer.getPoints()) // -> 0
21 PointsIncrementer.incrementPoints()
22 PointsIncrementer.addBonusPoints(100)
23 console.log(PointsIncrementer.getPoints()) // -> final result is 101

As you can see, our module to update the points is much more manageable than if we had the variables in a global scope. We can also export the PointsIncrementer, and be able to use it anywhere else it is needed in the project.


Hopefully these examples have demonstrated how IIFE's can be used and how they are useful in avoiding global variables. As a follow up exercise, try to extend the PointsIncrementer module so that totalPoints can be initialized with a default value passed in from the caller.

If this post was helpful in anyway, please consider signing up for my newsletter below ⬇️.