Demystifying JavaScript Closures, Callbacks and IIFEs

Ivaylo Gerchev
Share

We’ve already taken a close look at variable scope and hoisting, so today we’ll finish our exploration by examining three of the most important and heavily-used concepts in modern JavaScript development — closures, callbacks and IIFEs.

Closures

In JavaScript, a closure is any function that keeps reference to variables from its parent’s scope even after the parent has returned.

This means practically any function can be considered a closure, because, as we learned in the variable scope section from the first part of this tutorial, a function can refer to, or have access to –

  • any variables and parameters in its own function scope
  • any variables and parameters of outer (parent) functions
  • any variables from the global scope.

So, chances are you’ve already used closures without even knowing it. But our aim is not just to use them – it is to understand them. If we don’t understand how they work, we can’t use them properly. For that reason, we are going to split the above closure definition into three easy-to-comprehend points.

Point 1: You can refer to variables defined outside of the current function.

function setLocation(city) {
  var country = "France"; 

  function printLocation() {       
    console.log("You are in " + city + ", " + country);  
  }

  printLocation();
}

setLocation ("Paris");  // output: You are in Paris, France

Try out the example in JS Bin

In this code example, the printLocation() function refers to the country variable and the city parameter of the enclosing (parent) setLocation() function. And the result is that, when setLocation() is called, printLocation() successfully uses the variables and parameters of the former to output “You are in Paris, France”.

Point 2: Inner functions can refer to variables defined in outer functions even after the latter have returned.

function setLocation(city) {
  var country = "France"; 

  function printLocation() {       
    console.log("You are in " + city + ", " + country);  
  }

  return printLocation;
}

var currentLocation = setLocation ("Paris");   

currentLocation();   // output: You are in Paris, France

Try out the example in JS Bin

This is almost identical to the first example, except that this time printLocation() is returned inside the outer setLocation() function, instead of being immediately called. So, the value of currentLocation is the inner printLocation() function.

If we alert currentLocation like this – alert(currentLocation); – we’ll get the following output:

function printLocation () {       
  console.log("You are in " + city + ", " + country);  
}

As we can see, printLocation() is executed outside its lexical scope. It seems that setLocation() is gone, but printLocation() still has access to, and “remembers”, its variable (country) and parameter (city).

A closure (inner function) is able to remember its surrounding scope (outer functions) even when it’s executed outside its lexical scope. Therefore, you can call it at any time later in your program.

Point 3: Inner functions store their outer function’s variables by reference, not by value.

function cityLocation() {
  var city = "Paris";

  return {
    get: function() { console.log(city); },  
    set: function(newCity) { city = newCity; }
  };
}

var myLocation = cityLocation();

myLocation.get();           // output: Paris
myLocation.set('Sydney');
myLocation.get();           // output: Sydney

Try out the example in JS Bin

Here cityLocation() returns an object containing two closures – get() and set() – and they both refer to the outer variable city. get() obtains the current value of city, while set() updates it. When myLocation.get() is called for the second time, it outputs the updated (current) value of city – “Sydney” – rather than the default “Paris”.

So, closures can both read and update their stored variables, and the updates are visible to any closures that have access to them. This means that closures store references to their outer variables, rather than copying their values. This is a very important point to remember, because not knowing it can lead to some hard-to-spot logic errors – as we’ll see in the “Immediately Invoked Function Expressions (IIFEs)” section.

One interesting feature of closures is that the variables in a closure are automatically hidden. Closures store data in their enclosed variables without providing direct access to them. The only way to alter those variables is by providing access to them indirectly. For example, in the last piece of code we saw that we can modify the variable city only obliquely by using the get() and set() closures.

We can take advantage of this behavior to store private data in an object. Instead of storing the data as an object’s properties, we can store it as variables in the constructor, and then use closures as methods that refer to those variables.

As you can see, there is nothing mystical or esoteric around the closures – only three simple points to remember.

Callbacks

In JavaScript, functions are first-class objects. One of the consequences of this fact is that functions can be passed as arguments to other functions and can also be returned by other
functions.

A function that takes other functions as arguments or returns functions as its result is called a higher-order function, and the function that is passed as an argument is called a callback function. It’s named “callback” because at some point in time it is “called back” by the higher-order function.

Callbacks have many everyday usages. One of them is when we use the setTimeout() and setInterval() methods of the browser’s window object – methods that accept and execute callbacks:

function showMessage(message){
  setTimeout(function(){
    alert(message);
  }, 3000);  
}

showMessage('Function called 3 seconds ago');

Try out the example in JS Bin

Another example is when we attach an event listener to an element on a page. By doing that we’re actually providing a pointer to a callback function that will be called when the event occurs.

// HTML

<button id='btn'>Click me</button>

// JavaScript

function showMessage(){
  alert('Woohoo!');
}

var el = document.getElementById("btn");
el.addEventListener("click", showMessage);

Try out the example in JS Bin

The easiest way to understand how higher-order functions and callbacks work is to create your own. So, let’s create one now:

function fullName(firstName, lastName, callback){
  console.log("My name is " + firstName + " " + lastName);
  callback(lastName);
}

var greeting = function(ln){
  console.log('Welcome Mr. ' + ln);
};

fullName("Jackie", "Chan", greeting);

Try out the example in JS Bin

Here we create a function fullName() that takes three arguments – two for the first and last name, and one for the callback function. Then, after the console.log() statement, we put a function call that will trigger the actual callback function – the greeting() function defined below the fullName(). And finally, we call fullName(), where greeting() is passed as a variable – without parentheses – because we don’t want it executed right away, but simply want to point to it for later use by fullName().

We are passing the function definition, not the function call. This prevents the callback from being executed immediately, which is not the idea behind the callbacks. Passed as function definitions, they can be executed at any time and at any point in the containing function. Also, because callbacks behave as if they are actually placed inside that function, they are in practice closures: they can access the containing function’s variables and parameters, and even the variables from the global scope.

The callback can be an existing function as shown in the preceding example, or it can be an anonymous function, which we create when we call the higher-order function, as shown in the following example:

function fullName(firstName, lastName, callback){
  console.log("My name is " + firstName + " " + lastName);
  callback(lastName);
}

fullName("Jackie", "Chan", function(ln){console.log('Welcome Mr. ' + ln);});

Try out the example in JS Bin

Callbacks are heavily used in JavaScript libraries to provide generalization and reusability. They allow the library methods to be easily customized and/or extended. Also, the code is easier to maintain, and much more concise and readable. Every time you need to transform your unnecessary repeated code pattern into more abstract/generic function, callbacks come to the rescue.

Let’s say we need two functions – one that prints information about published articles and another that prints information about sent messages. We create them, but we notice that some part of our logic is repeated in both of the functions. We know that having one and the same piece of code in different places is unnecessary and hard to maintain. So, what is the solution? Let’s illustrate it in the next example:

function publish(item, author, callback){   // Generic function with common data
  console.log(item);
  var date = new Date();

  callback(author, date);
}

function messages(author, time){   // Callback function with specific data
  var sendTime = time.toLocaleTimeString();
  console.log("Sent from " + author + " at " + sendTime);
}

function articles(author, date){   // Callback function with specific data
  var pubDate = date.toDateString();
  console.log("Written by " + author);
  console.log("Published " + pubDate);
}

publish("How are you?", "Monique", messages);

publish("10 Tips for JavaScript Developers", "Jane Doe", articles);

Try out the example in JS Bin

What we’ve done here is to put the repeated code pattern (console.log(item) and var date = new Date()) into a separate, generic function (publish()), and leave only the specific data inside other functions – which are now callbacks. That way, with one and the same function we can print information for all sorts of related things – messages, articles, books, magazines and so on. The only thing you need to do is to create a specialized callback function for each type, and pass it as an argument to the publish() function.

Immediately Invoked Function Expressions (IIFEs)

An immediately invoked function expression, or IIFE (pronounced “iffy”), is a function expression (named or anonymous) that is executed right away after its creation.

There are two slightly different syntax variations of this pattern:

// variant 1

(function () {
  alert('Woohoo!');
})();

// variant 2

(function () {
  alert('Woohoo!');
}());

To turn a regular function into an IIFE you need to perform two steps:

  1. You need to wrap the whole function in parentheses. As the name suggests, an IIFE must be a function expression, not a function definition. So, the purpose of the enclosing parentheses is to transform a function definition into an expression. This is because, in JavaScript, everything in parentheses is treated as an expression.
  2. You need to add a pair of parentheses at the very end (variant 1), or right after the closing curly brace (variant 2), which causes the function to be executed immediately.

There are also three more things to bear in mind:

First, if you assign the function to a variable, you don’t need to enclose the whole function in parentheses, because it is already an expression:

var sayWoohoo = function () {
  alert('Woohoo!');
}();

Second, a semicolon is required at the end of an IIFE, as otherwise your code may not work properly.

And third, you can pass arguments to an IIFE (it’s a function, after all), as the following example demonstrates:

(function (name, profession) {
  console.log("My name is " + name + ". I'm an " + profession + ".");
})("Jackie Chan", "actor");   // output: My name is Jackie Chan. I'm an actor.

Try out the example in JS Bin

It’s a common pattern to pass the global object as an argument to the IIFE so that it’s accessible inside of the function without having to use the window object, which makes the code independent of the browser environment. The following code creates a variable global that will refer to the global object no matter what platform you are working on:

(function (global) {
  // access the global object via 'global'
})(this);
</code></pre>

<p>This code will work both in the browser (where the global object is <code>window</code>), or in a Node.js environment (where we refer to the global object with the special variable <code>global</code>). </p>

<p>One of the great benefits of an IIFE is that, when using it, you don’t have to worry about polluting the global space with temporary variables. All the variables you define inside an IIFE will be local. Let’s check this out:</p>

[code language="javascript"](function(){

  var today = new Date();
  var currentTime = today.toLocaleTimeString();
  console.log(currentTime);   // output: the current local time (e.g. 7:08:52 PM)

})();

console.log(currentTime);   // output: undefined

Try out the example in JS Bin

In this example, the first console.log() statement works fine, but the second fails, because the variables today and currentTime are made local thanks to the IIFE.

We know already that closures keep references to outer variables, and thus, they return the most recent/updated values. So, what do you think is going to be the output of the following example?

function printFruits(fruits){
  for (var i = 0; i &lt; fruits.length; i++) {
    setTimeout( function(){
      console.log( fruits[i] );
    }, i * 1000 );
  }
}

printFruits(["Lemon", "Orange", "Mango", "Banana"]);

Try out the example in JS Bin

You may have expected that the names of the fruits would be printed one after another at one-second intervals. But, in practice, the output is four times “undefined”. So, where is the catch?

The catch is that the value of i, inside the console.log() statement, is equal to 4 for each iteration of the loop. And, since we have nothing at index 4 in our fruits array, the output is “undefined”. (Remember that, in JavaScript, an array’s index starts at 0.) The loop terminates when i < fruits.length returns false. So, at the end of the loop the value of i is 4. That most recent version of the variable is used in all the functions produced by the loop. All this happens because closures are linked to the variables themselves, not to their values.

To fix the problem, we need to provide a new scope – for each function created by the loop – that will capture the current state of the i variable. We do that by closing the setTimeout() method in an IIFE, and defining a private variable to hold the current copy of i.

function printFruits(fruits){
  for (var i = 0; i &lt; fruits.length; i++) {
    (function(){
      var current = i;                    // define new variable that will hold the current value of "i"
      setTimeout( function(){
        console.log( fruits[current] );   // this time the value of "current" will be different for each iteration
      }, current * 1000 );
    })();
  }
}

printFruits(["Lemon", "Orange", "Mango", "Banana"]);

Try out the example in JS Bin

We can also use the following variant, which does the same job:

function printFruits(fruits){
  for (var i = 0; i &lt; fruits.length; i++) {
    (function(current){
      setTimeout( function(){
        console.log( fruits[current] );
      }, current * 1000 );
    })( i );
  }
}

printFruits(["Lemon", "Orange", "Mango", "Banana"]);

Try out the example in JS Bin

An IIFE is often used to create scope to encapsulate modules. Within the module there is a private scope that is self-contained and safe from unwanted or accidental modification. This technique, called the module pattern, is a powerful example of using closures to manage scope, and it’s heavily used in many of the modern JavaScript libraries (jQuery and Underscore, for example).

Conclusion

The aim of this tutorial has been to present these fundamental concepts as clearly and concisely as possible – as a set of simple principles or rules. Understanding them well is key to being a successful and productive JavaScript developer.

For a more detailed and in-depth explanation of the topics presented here, I recommend you take a look at Kyle Simpson’s You Don’t Know JS: Scope & Closures.