JS Closures

In the JavaScript functions tutorial, you have learned that in JavaScript a variable's scope can be global or local. Though, with ES6 you can also create block-scoped variables using the let keyword.

A local variable can only be accessed and manipulated by the function they are declared in, while a global variable can be accessed and manipulated anywhere in the program.

In programming, certain situations require you to make a variable available throughout the script, and you don't want just any part of your code to be able to change its value accidentally.

 

Global Variable

Now, let's see what happens when you attempt to achieve this using the global variable:

// Global variable
var counter  = 0;

// A function dedicated to manipulate the 'counter' variable
function makeCounter() {
    return counter += 1;
}

// Calling the function
makeCounter();
console.log(counter); // Prints: 1

makeCounter();
console.log(counter); // Prints: 2

// Trying to manipulate the 'counter' variable from outside
counter = 10;
console.log(counter); // Prints: 10

Try with example

 

From the example above, you can see that the value of the counter variable can be changed from anywhere in the program, without calling the makeCounter() function.

Now, let's use the local variable and see what happens:

function makeCounter() {
    // Local variable
    var counter  = 0;
	
    // Manipulating the 'counter' variable
    return counter += 1;
}

// Calling the function
console.log(makeCounter()); // Prints: 1
console.log(makeCounter()); // Prints: 1

Try with example

In this example, the counter variable cannot be manipulated from outside, because it is local to makeCounter() function, and its value will also not increase after a subsequent function call, because every time we call the function it reset the counter variable value, which you can clearly see in line no-11. However, the JavaScript closure can solve our problem.

A closure is an inner function that has access to the parent function's scope, even when the parent function has finished executing. It is accomplished by creating a function inside another function. This is shown in the example below:

function makeCounter() {
    var counter = 0;
	
    // Nested function
    function make() {
        counter += 1;
        return counter;
    }
    return make;
}

/* Execute the makeCounter() function and store the
returned value in the myCounter variable */
var myCounter = makeCounter();

console.log(myCounter()); // Prints: 1
console.log(myCounter()); // Prints: 2

Try with example

From the above example, you can see that the inner function make() is returned from the outer function makeCounter(). Hence, the value of the ‘myCounter’ is the inner make() function (line no-14), and calling myCounter effectively calls make(). In JavaScript functions can be assigned to variables, passed as arguments to other functions, can be nested inside other functions, and many more.

Also, you will notice that the inner function make() can still access the value of the counter variable defined in the outer function, even though the makeCounter() function has already finished executing (as in line no-14). This happened because functions in JavaScript form closures. However, closures internally store references to their outer variables and can access and update their values.

Also, the example, the make() function is a closure whose code refers to the outer variable counter. It implies that whenever the make() function is called, the code inside it is capable to access and update the counter variable because it is stored in the closure.

Lastly, from the example, as the outer function has finished executing, no other part of the code can access or manipulate the counter variable. Only the inner function has exclusive access to it.

The previous example can also be written using an anonymous function expression, as shown here:

// Anonymous function expression
var myCounter = (function() {
    var counter = 0;
	
    // Nested anonymous function
    return function() {
        counter += 1;
        return counter;
    }
})();

console.log(myCounter()); // Prints: 1
console.log(myCounter()); // Prints: 2

Try with example

Tip: All functions in JavaScript have access to the global scope, and also the scope above them. Since nested functions are supported by JavaScript, this means that the nested functions have access to any value declared in a higher scope including its parent function's scope.

Note: The global variables live as long as your web page lives. While, the local variables have a short life span, as they are created when the function is called and destroyed as soon as the function is finished executing.

 

Creating the Getter and Setter Functions

Here you learn how to will create a variable secret and protect it from being directly manipulated by outside code using a closure. We will also create getter and setter functions to get and set its value.

More so, the setter function will also perform a quick check whether the specified value is a number or not, however, if it is not, it will not change the variable value.: 10

var getValue, setValue;

// Self-executing function
(function() {
    var secret = 0;
    
    // Getter function
    getValue = function() {
        return secret;
    };
    
    // Setter function
    setValue = function(x) {
        if(typeof x === "number") {
            secret = x;
        }
    };
}());

// Calling the functions
getValue(); // Returns: 0
setValue(10);
getValue(); // Returns: 10
setValue(null);
getValue(); // Returns: 10

Try with example

 

Tip: The self-executing functions are also called ‘immediately invoked function expression’ (IIFE). That is, immediately executed function, or self-executing anonymous function.