October 25, 2020 • 2 min read
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.
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.
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.
1let totalPoints = 023function countPoints() {4totalPoints += 15return totalPoints6}78// After each call the totalPoints gets updated by 1,9// doing what we expected it to.10console.log(countPoints()) // -> 111console.log(countPoints()) // -> 212console.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 be3// called like any other function.4const incrementPoints = (function(pointsEarned) {5let totalPoints = 067return function () {8totalPoints += pointsEarned9return totalPoints10}11})(10)1213// After each call, the totalPoints gets updated by 10, the value of the14// pointsEarned parameter.15console.log(incrementPoints()) // -> 1016console.log(incrementPoints()) // -> 2017console.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
.
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.
1const PointsIncrementer = (function() {2let totalPoints = 034return {5incrementPoints: function() {6totalPoints += 1;7},8decrementPoints: function() {9totalPoints -= 1;10},11addBonusPoints: function (bonusAmount) {12totalPoints += bonusAmount13},14getPoints: function () {15return totalPoints16}17}18})()1920console.log(PointsIncrementer.getPoints()) // -> 021PointsIncrementer.incrementPoints()22PointsIncrementer.addBonusPoints(100)23console.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 ⬇️.