JavaScript Closures and IIFE Functions

Closures

A closure is a construct where a function created in one scope remembers its lexical environment even when it executes outside its scope.

A closure technically has three components:

an external function that defines some scope and in which some variables are defined – lexical environment

variables (lexical environment) that are defined in the outer function

the nested function that uses these variables

function outer(){       // outer function
    var n;              // some variable
    return inner(){     // nested function
        // actions with variable n
    }
}

Consider closures using the simplest example:

function outer(){
    let x = 5;
    function inner(){
        x++;
        console.log(x);
    };
    return inner;
}
let fn = outer();   // fn = inner, since the outer function returns the inner function
// call inner inner function
fn();   // 6
fn();   // 7
fn();   // 8

Here, the outer function defines the scope in which the inner function inner and the x variable are defined. The variable x represents the lexical environment for the inner function. In the inner function itself, we increment the variable x and print its value to the console. At the end, the outer function returns the inner function.

Next, we call the outer function:

let fn = outer();

Since the outer function returns the inner function, the fn variable will store a reference to the inner function. At the same time, this function remembered its environment – that is, the external variable x.

Next, we actually call the Inner function three times, and we see that the variable x, which is defined outside the inner function, is incremented:

fn();   // 6
fn();   // 7
fn();   // 8

That is, despite the fact that the variable x is defined outside the inner function, this function remembers its environment and can use it, despite the fact that it is called outside the outer function in which it was defined. This is the essence of closures.

Let’s look at another example:

 
function multiply(n){
    var x = n;
    return function(m){ return x * m;};
}
var fn1 = multiply(5);
var result1 = fn1(6); // 30
console.log(result1); // 30
 
var fn2= multiply(4);
var result2 = fn2(6); // 24
console.log(result2); // 24

So here, calling a function multiply()results in calling another internal function. The internal function is:

 
function(m){ return x * m;};

remembers the environment in which it was created, in particular, the value of x.

As a result, when the multiply function is called, the variable is defined fn1, which is a closure, that is, it combines two things: the function and the environment in which the function was created. The environment consists of any local variable that was in the scope of the function multiplied at the time the closure was created.

That is fn1, it is a closure that contains both the internal function function(m){ return x * m;}and the x variable that existed at the time the closure was created.

When creating two closures: fn1and fn2, each of these closures creates its own environment.

It is important not to get confused about the parameters. When defining a closure:

var fn1 = multiply(5);

The number 5 is passed as parameter n to the multiply function.

When calling an internal function:

 
var result1 = fn1(6);

The number 6 is passed for parameter m to the inner function function(m){ return x * m;};.

We can also use another option to call the closure:

 
function multiply(n){
    var x = n;
    return function(m){ return x * m;};
}
var result = multiply(5)(6); // 30
console.log(result);
Self-calling functions

Usually, the definition of a function is separated from its call: first, we define the function, and then we call it. But this is optional. We can also create functions that will be called immediately upon definition. Such functions are also called Immediately Invoked Function Expressions (IIFE).

(function(){
    console.log("Hello World");
}());
 
(function (n){
     
    var result = 1;
    for(var i=1; i<=n; i++)
        result *=i;
    console.log("Factorial numbers " + n + " equal to " + result);
}(4));

Such functions are enclosed in parentheses, and after the function definition, the parameters are passed in parentheses.