IIFEs: Immediately Invoked Function Expressions

Prior to the addition of modules to the JavaScript language, the best practice to get the desired separation and sharing was to use the namespace of functions. So, all the the global variables, like tan, instead become local variables of a function. We saw this in our reading on Modules, in the section on namespaces and functions with a function called namespace2.

In this supplemental reading, we'll see how functions can be used as namespaces, giving us both the separation and sharing that we want, though it'll be tougher going than JavaScript's module system.

This reading explains and expands on "The Module Pattern", which starts on page 165 of our book.

There are two approaches I could take with this presentation: I could start with properties of functions and build a module system from first principles, or I could try to bridge the gap between the modules we want and the functions we have. I'll do a little of both.

Functions as Containers

Let's start with functions. As you know, a function can have local variables (even ones with the same name as global variables) and those variables are distinct and separate. A function is its own little world.

Here's a function that adds some special sauce to its argument. The sauce is separate from the global variable with the same name:

var sauce = 5;

function addSauce(x) {
    var sauce = 3;
    return x + sauce;
}

Invoking addSauce(1) returns 4, not 6.

Thus, a function creates a namespace.

Globals

But what if we really need a global variable? We might need a global to count something, say the number of times a user has clicked on something or the number of times a function has been invoked.

Let's take a specific example. Suppose we want to annoy the user, playing a prank on them, requiring them to feed the cookie monster some number of cookies. To feed the cookie monster, we pop up an alert every 5 seconds that says "give me a cookie" and user has to click "okay". So that it eventually stops, we count using a global variable. Here's the code.


var maxCount = 10;

var currCount = 0;

function cookieMonster() {
    currCount ++;
    if( currCount < maxCount ) {
        alert('Give me a cookie!');
        setTimeout(cookieMonster, 5000);
    }
}

// start it
setTimeout(cookieMonster, 5000);

Note that the code uses the built-in function setTimeout which runs a function of our choosing every N milliseconds (5000 milliseconds is 5 seconds), where the function is the first argument and the delay is the second argument. You'll see that function again in a more reasonable way when we do automatic slide shows.

Enclosing the Globals

The prank code works fine. (Copy/paste it into your browser console if you want. You can stop it by closing or reloading that browser tab.) But suppose that we want to avoid the global variables. First of all, this is an example of modules. Secondly, maybe there are other things we want to count, or maybe we want to keep the user from modifying the currCount and maxCount variables.

What we can do is turn all of these names (the two variables and the function) into local names of a surrounding "wrapper" function that they are all inside:

function prank() {

    var maxCount = 10;

    var currCount = 0;

    function cookieMonster() {
        currCount ++;
        if( currCount < maxCount ) {
            alert('Give me a cookie!');
            setTimeout(cookieMonster, 5000);
        }
    }

    // start it
    setTimeout(cookieMonster, 5000);
}
prank();

Again, you can try this; it works.

IIFE

You'll notice our prank function is defined (globally) only to be immediately invoked. The main purpose of naming something is to refer to it later or elsewhere, but neither of those really apply here.

Here's a concrete example. In a function to convert fahrenheit to celcius, which do you prefer:

function fahrenheit2celsius(f) {
    var diff = f - 32;
    return 1.8 * diff;
}

function fahrenheit2celsius(f) {
    return 1.8 * (f - 32);
}

If you like the extra variable and the extra line of code, you're welcome to do so, but you can see that it's not necessary, and the latter code is certainly acceptable.

We can do the same thing with our prank function. Instead of naming it, we'll make it anonymous, but we'll wrap the definition in parentheses and then use a pair of parentheses to invoke it:

(function () {

    var maxCount = 10;

    var currCount = 0;

    function cookieMonster() {
        currCount ++;
        if( currCount < maxCount ) {
            alert('Give me a cookie!');
            setTimeout(cookieMonster, 5000);
        }
    }

    // start it
    setTimeout(cookieMonster, 5000);
})();

Technically, by wrapping the definition in parentheses, we've converted a function statement into an expression (something that has a value), and we are immediately invoking it. Thus, we have an immediately invoked function expression. That's such a cumbersome phrase, that it's just called an IIFE. Wikipedia has more about Immediately Invoked Function Expressions.

Using an IIFE means that no names are added to the global namespace, so the code has no footprint at all. (The footprint of code is the set of names that are added to the global JavaScript namespace.)

Abstractly, an IIFE is like this:

(function () {
    ...
})();

Observations:

  • the punctuation on that last line is terrifying
  • the IIFE is just a simple wrapper around normal code. We didn't change our prank code at all. We just cut/pasted it into an IIFE.
  • No names from the inside of the IIFE leak out into the global namespace. The IIFE doesn't define any global names. Indeed, if it weren't for the alerts, it would be as if this code didn't exist.

The last point is worth emphasizing: an IIFE gives us a completely separate namespace in an easy way, just requiring two extra lines of code, before and after our code.

IIFEs Today

In fact, IIFEs are so good at creating these little worlds with their own namespaces, and without the need for separate files, imports and exports, and all that, that IIFEs are still used today for lightweight, easy modules.

Sharing

What we want out of a module system is separation and sharing. The IIFE we had above works very well at the separation part, but how can it share?

Two techniques are possible and are used. One is to return a value from the IIFE that contains all the things we want to share. For example, our prank code above runs automatically. What if we want to load the code, keep its global variables separate, but share the cookieMonster function? We could return the function, and allow the caller to assign it to variable of their choosing. This requires us to put the IIFE on the right hand side of an assignment statement:

var myPrank = (function () {

var maxCount = 10;

var currCount = 0;

function cookieMonster() {
    currCount ++;
    if( currCount < maxCount ) {
        alert('Give me a cookie!');
        setTimeout(cookieMonster, 5000);
    }
}

return cookieMonster;
})();

Then, when we want to run the prank, we invoke the myPrank function.

This can work, but the syntax is awkward, and it doesn't scale. What if we wanted to export two or more functions, say startPrank and stopPrank? While it's possible to do more in this direction, our book uses a different approach, which we'll turn to now.

Creating Global Names

This section explains what's going on in section "Adding Modules to a Namespace", starting on page 170 of our book.

As we learned in the main reading, there's a global namespace:

  • it's an object
  • all global variables are properties of that object and
  • all properties of that object are global variables

In browsers at the time our book was written, that global object is the value of window. Therefore, if our prank code wanted to create two globals, it can do so by assigning to properties of window, like this:

(function () {
   ...
   function startPrank() { ... }

   function stopPrank() { ... }

   // create some globals
   window.startPrank = startPrank;
   window.stopPrank = stopPrank;
})();

Those final assignment statements are a little like the export statements in modern JavaScript modules, since they have the effect of making certain names from the "inside" available on the "outside".

However, there's a small problem. You knew it wasn't going to be that easy, didn't you?

The Variable with the Global Environment

It turns out that different JavaScript implementations used different variables to hold that global environment. Browsers used window, as we know. Web Workers used self. Node.js used this. What to do about this heterogeneous world?

(One solution is to change the language to introduce a standard name for the global environment. That's the genesis of globalThis, which we used in the main modules reading. The globalThis variable didn't exist in JavaScript at the time our book was written.)

Given the lack of a consistent name, how can we write code that can be easily made to work in Web Workers, Node.js and a browser?

You might think that all we have to do is global search and replace, renaming all the occurrences of window to some other name. We could, but that would be error prone. Suppose we have a variable named windowShade. Do we really want it renamed to be thisShade? Probably not. But it turns out we don't need that. We can use ability of functions to rename values.

Functions Can Rename

Functions create a local namespace, and that namespace can be a new set of names. We can use that to do a kind of structured renaming.

Suppose we choose a neutral name for the global environment, not window, self, or this. Let's say we use glob to hold it. Our export statements then become:

   // create some globals
   glob.startPrank = startPrank;
   glob.stopPrank = stopPrank;

Okay, but how do we actually make that neutral code work? Imagine creating a simple, anonymous function that takes one argument, called glob, which we can bind to whatever value we want:

function (glob) { 
   // create some globals
   glob.startPrank = startPrank;
   glob.stopPrank = stopPrank;
}

Now, if that code is running in a browser, we want glob to have the value window. That's easy enough:

(function (glob) { 
   // create some globals
   glob.startPrank = startPrank;
   glob.stopPrank = stopPrank;
})(window);

See how the function is invoked with window as its argument? That means glob will have window as its value and our code works perfectly.

If we decide to port our code to run in, say, Web Workers which uses self, we only have to change that last line:

(function (glob) { 
   // create some globals
   glob.startPrank = startPrank;
   glob.stopPrank = stopPrank;
})(self);

Our book's authors, however, decided again choosing a "neutral" name like glob. First of all, trying to come up with a neutral name is problematic at best: it's a new name to clutter up our minds. Second, we're writing code for a normal browser, not for for Web Workers or Node.js, so our variable will always have the value of window. We just want to allow for the possibility that it might have a different value. Third, we'd have to remember that glob means window.

So, with that in mind, our authors used the renaming trick, but they used window as the local name. So their code looks like:

(function (window) { 
   // create some globals
   window.startPrank = startPrank;
   window.stopPrank = stopPrank;
})(window);

This can be confusing, so let's pause to think about this for a moment. The window on the last line is outside our IIFE and is the normal window variable, which contains the global environment. The window on the first line is inside our IIFE, and is just as much a local variable of the module as maxCount, currCount, startPrank, stopPrank or anything else inside our IIFE.

Our book does this all the time, so it's worth thinking it through at least once.

If we wanted to run this code on Web Workers, we would just change the last line:

(function (window) { 
   // create some globals
   window.startPrank = startPrank;
   window.stopPrank = stopPrank;
})(self);

That shows the renaming in action: the function invocation renames window to self throughout the IIFE.

Creating A Module

Our book's authors wants to do a few more things, namely

  1. have the modules put themselves in a global App variable, and
  2. allow the modules to be loaded in any order.

The first goal is nice, because then everything is available via just one variable, App, so the program's possible conflicts with other code is minimized. The second is also nice because it means one less thing to remember as we write our main HTML page or our main module.

Without the goal of order-independent loading of files, it would be straightforward to create the object and have each file add to it. The first module file creates the object:

var App = {};  // local variable with empty object at first

App.CheckList = CheckList;  // store something in the App

window.App = App; // make the App object global

The subsequent modules do this:

var App = window.App; // local variable with the global App object

App.DataStore = DataStore;  // store something in the App

window.App = App;           // make the App object globala

Notice that the last lines are all the same; they don't care whether they are loaded first or not. Only the first line is affected.

That first line initializes a local variable named App to contain either a new, empty object, or the global App object that some prior module created. The last line sets the global App variable to be the same object as the local variable of the same name.

So, how can we write code that handles either being the first module or a subsequent module? It turns out that, if the global value doesn't exist, it counts as false, so we can use a logical or operator (||) to give a default value if the global value doesn't exist.

Therefore, all our modules start with this:

var App = window.App || {}; // init local variable

In English, this just says "use the existing value if there is one, otherwise, use a new, empty object". Since all of our modules start this way, they can be loaded in any order.

Final Words

Here's the code from page 171 of our book:

(function (window) {
  'use strict';
   var App = window.App || {};

   function DataStore() {
      console.log('running the DataStore function');
   }

   App.DataStore = DataStore;
   window.App = App;
})(window);

Eleven lines of code that don't make a lot of sense without a deep understanding of IIFEs, namespaces, the global environment and default values. Hopefully, that code makes a bit more sense to you now.

Rewritten Coffeerun

Note that in my re-write of the Coffeerun app, I didn't write each module to add itself to the App object. Instead, I had the main-module.js, which loads all the modules (order-independent), add each to the App object:

import { CheckList, makeDescription, makeRowElt } from './checklist-module.js';
import { Validation } from './validation-module.js';
import { FormHandler } from './formhandler-module.js';
import { RemoteDataStore } from './remotedatastore-module.js';
import { DataStore } from './datastore-module.js';
import { Truck } from './truck-module.js';

var App = {};                   // initially empty
globalThis.App = App;           // for debugging

// Add these names to the App
App.CheckList = CheckList;
App.makeDescription = makeDescription;
App.makeRowElt = makeRowElt;
App.Validation = Validation;
App.FormHandler = FormHandler;
App.RemoteDataStore = RemoteDataStore;
App.DataStore = DataStore;
App.Truck = Truck;

I did this partly for simplicity in the various modules and also to avoid having the modules depend on any expectations of being part of something called App. A Python module doesn't add itself to something called App but is more generic. These rewritten modules are more in that spirit.