DEV Community

V
V

Posted on

Immediately Invoked Function Expressions (IIFE) in JavaScript

Immediately Invoked Function Expression...sounds like a pretty intense term! Let's take a second to break it down.

  • Function Expression: a function that is stored in a variable
  • Invoked: the function has been executed

This is how we typically see a function expression set up in JavaScript:

  const eatFruit = function(){
    console.log('I took a bite of the apple');
  }

  eatFruit();  // I took a bite of the apple
Enter fullscreen mode Exit fullscreen mode

If we want to invoke that function, we call eatFruit(). The parenthesis at the end of the function name is what is invoking it, which in this case would log "I took a bite of the apple."

Now we will look at an Immediately Invoked Function Expression (IIFE, which is pronounced "iffy"):

(function () {
    console.log('I happen immediately!') // I happen immediately!
})();
Enter fullscreen mode Exit fullscreen mode

To invoke an IIFE, all we need to do is place the parenthesis at the end of the expression. There is no need to make a separate call to this function.

 

What is the tie between a function expression and an IIFE?

  • Function expressions give us the ability to store a function in a variable, while a function declaration does not. A function declaration expects different syntax.
  • The parenthesis that wrap the function allow JavaScript to treat this syntax as a function expression vs a function declaration. If the parenthesis are not present, JavaScript will throw an error because it will try to interpret it as a function declaration.

The below example does not work and will throw an error. Notice that the parenthesis that wrap an IIFE are not present.

function () {
    console.log('I happen immediately!')
}();
// "SyntaxError: Function statements require a function name."
Enter fullscreen mode Exit fullscreen mode

 

Why use an IIFE?

I know at this point you're probably wondering why you would ever use this. Can't we also achieve the same results by creating either a function declaration or function expression and invoking it immediately after its creation? Well, kind of. There are a few areas that differ between a typical function declaration or expression vs an IIFE.

Many of the problems that IIFEs solve have been eliminated due to ES6 and the recent updates to the language, but I do think it's still important to point out some of its capabilities and to recognize them.

Private Data

IIFEs allow for private variables. This isn't so much of an issue now because with ES6 and the latest JavaScript updates, we are able to use const and let for block scoping. When var was the only option, we could have naming collisions with the global scope. Let's look at an example.

{
  var favoriteFood = 'French Fries';
}
console.log(favoriteFood) // French Fries
Enter fullscreen mode Exit fullscreen mode

Wrapping a variable in curly brackets creates a block, but var does not allow block scoping. If you try the example above, you will see that the variable can still be accessed outside of the block and will log "French Fries".

Next try this with let or const.

{
  const favoriteFood = 'French Fries';
}
console.log(favoriteFood) 
// ReferenceError: favoriteFood is not defined
Enter fullscreen mode Exit fullscreen mode

Here we will get a reference error since both let and const can be block scoped. This helps us make variables private and prevents naming collisions in the global scope. Before let and const were created, this scoping issue could be solved by using IIFEs since var can be function scoped.

Below you can see that the variable favoriteFood is no longer accessible to the global scope.

(function(){
    var favoriteFood = 'French Fries'
})();
console.log(favoriteFood)
// ReferenceError: favoriteFood is not defined
Enter fullscreen mode Exit fullscreen mode

Another example of keeping variables private can be demonstrated with functions inside of an IIFE.

(function counter() {
    var count = 0;

    function increment() {
      count++;
    }

    increment();
    console.log(count) // 1
}());
Enter fullscreen mode Exit fullscreen mode

We now have created a private function which can modify our private variables. We are able to access count inside of the function within the IIFE!

Closures

The last example I will provide is a little bit more of an advanced concept and involves the topic of closures. I'm not going to go into detail on closures in this post, but here is an example to demonstrate how IIFEs and closures work together.

const counter = (function add() {
    let count = 0;

    return function() {
      count++;
      return count;
    }
}());

console.log(counter()) // 1
console.log(counter()) // 2
console.log(counter()) // 3
Enter fullscreen mode Exit fullscreen mode

Here's what is happening:

  1. The IIFE "add" initializes the variable "count" at 0
  2. It then returns a function that adds 1 to the "count" variable.
  3. That function also returns "count".
  4. Since "add" is an IIFE, it is immediately invoked and the result is stored in the variable "counter"
  5. When we log the value of counter multiple times, you can see that the value continues to increment each time. This shows an example of a closure.

This is not possible with a "typical" function expression! The following examples demonstrate this:

Below when we log the result of the counter function, the internal function is logged.

const counter = function add() {
    let count = 0;

    return function() {
      count++;
      return count;
    }
};

console.log(counter()); // function() { count++; return count; }
Enter fullscreen mode Exit fullscreen mode

If we want to run the internal function, we can invoke our function again! But even with this, we still do not get the results we get when using an IIFE.

const counter = function add() {
    let count = 0;

    return function() {
      count++;
      return count;
    }
};
// Notice below the extra set of parenthesis, which invokes the internal function!
console.log(counter()()); // 1
console.log(counter()()); // 1
console.log(counter()()); // 1
Enter fullscreen mode Exit fullscreen mode

 

Quick Recap

There are many more examples that exist for the use of IIFEs and the conversation could go on forever! Some developers say they no longer have as much of a purpose, others say there are still benefits to using them. I wanted to write this post to introduce others to the syntax of IIFEs and some of the capabilities associated with them.

Here is our recap on IIFEs:

  • Interpreted as function expressions vs function declarations
  • Do not require us to invoke the function separately
  • Provide us with better scoping capabilities, but not much of an issue any longer because of let and const in ES6
  • Can be used to take advantage of closures

Top comments (0)