DEV Community

Cover image for What the Hell is Closure in JavaScript?
Royal Jain for CodeParrot

Posted on

What the Hell is Closure in JavaScript?

You might have heard the term "closure" tossed around in JavaScript conversations and tutorials, but what the hell is it really?

What is a Closure?

In the simplest terms, a closure is a function that remembers the variables from the place where it was defined, regardless of where it is executed later. Let's break it down with an example:


function outerFunction() {
    let outerVariable = 'I am outside!';

    function innerFunction() {
        console.log(outerVariable);
    }

    return innerFunction;
}

const myInnerFunction = outerFunction();
myInnerFunction(); // Outputs: 'I am outside!'
Enter fullscreen mode Exit fullscreen mode

In this example, innerFunction is a closure. Why? Because it is a function defined within another function (outerFunction) and it has access to the variables of the outerFunction scope (in this case, outerVariable) even after outerFunction has finished executing.

When outerFunction gets called, it defines outerVariable, defines innerFunction, and then returns innerFunction. Even though outerFunction has completed execution and outerVariable would theoretically be out of scope and garbage collected, innerFunction still has access to outerVariable.

Why are Closures Useful?

Data Encapsulation:

You can use closures to create private variables and methods. This can be particularly useful in the modular design of your code.

function createCounter() {
    let count = 0;
    return {
        increment: function() {
            count++;
            return count;
        },
        decrement: function() {
            count--;
            return count;
        },
    };
}

const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.decrement()); // 1
Enter fullscreen mode Exit fullscreen mode

In this example, count acts as a private variable. It's accessible only to the increment and decrement functions but not from the outside world.

Maintaining State

In JavaScript, closures are often used to maintain state in asynchronous operations, event handlers, and more.

function setupAlertTimeout() {
    var message = 'Message after 3 seconds';
    setTimeout(function alertMessage() {
        alert(message);
    }, 3000);
}

setupAlertTimeout();
Enter fullscreen mode Exit fullscreen mode

Here, the alertMessage function maintains access to the message variable even after setupAlertTimeout has finished executing.

Currying

Closures allow the use of currying, which is a technique of evaluating functions with multiple arguments into sequences of functions with a single argument.


function multiply(a) {
    return function(b) {
        return a * b;
    };
}

const multiplyByTwo = multiply(2);
console.log(multiplyByTwo(4)); // 8
Enter fullscreen mode Exit fullscreen mode

In this case, multiplyByTwo is a closure that remembers the value of a (which is 2).

Try it out

Consider the following code snippet:

function outer() {
    var count = 0;
    return function inner() {
        count++;
        return count;
    };
}
var counter1 = outer();
var counter2 = outer();
console.log(counter1());
console.log(counter1());
console.log(counter2());
console.log(counter2());

Enter fullscreen mode Exit fullscreen mode

What will be the output of the above code and why?

Solution

In the provided code, two separate closures are created by calling outer() twice. Each closure maintains its own separate lexical environment, where the count variable is preserved between calls. Here's what happens step by step:

  1. var counter1 = outer(); creates the first closure. Inside this closure, count is initialized to 0.

  2. counter1() is called for the first time. This increments count to 1 inside the first closure and returns 1.

  3. counter1() is called again. This increments count to 2 inside the first closure and returns 2.

  4. var counter2 = outer(); creates the second closure. Inside this closure, a new, separate count is initialized to 0.

  5. counter2() is called for the first time. This increments the count in the second closure to 1 and returns 1.

  6. counter2() is called again. This increments the count in the second closure to 2 and returns 2. Therefore, the output of the code will be:

1
2
1
2
Enter fullscreen mode Exit fullscreen mode

A real world example

function showDelayedMessage(message, delay) {
    setTimeout(function() {
        console.log(message);
    }, delay);
}

showDelayedMessage('Hello after 3 seconds', 3000);
showDelayedMessage('Hello after 5 seconds', 5000);
Enter fullscreen mode Exit fullscreen mode

Writing a function like showDelayedMessage without leveraging closures would typically involve using global variables


var globalMessage; // Declare a global variable

function showDelayedMessageGlobal(message, delay) {
    globalMessage = message; // Assign the message to the global variable
    setTimeout(function() {
        console.log(globalMessage);
        globalMessage = null; // Clear the message after displaying it
    }, delay);
}

showDelayedMessageGlobal('Hello after 3 seconds', 3000);
Enter fullscreen mode Exit fullscreen mode

This examples illustrate that while you can avoid closures, doing so often leads to less desirable coding practices, such as global namespace pollution, especially when dealing with asynchronous events or encapsulating functionality. Closures provide a clean and effective way to encapsulate data with functions, which is why they are so widely used despite the alternative approaches available.

Top comments (7)

Collapse
 
jonrandy profile image
Jon Randy πŸŽ–οΈ • Edited

.. a closure is a function that remembers the variables from the place where it was defined,

This isn't correct. A closure is not a function, and ALL functions form closures with their surrounding scope, there isn't a special type of function that does this.

Collapse
 
royaljain profile image
Royal Jain

The diagram in your article is very good. Function + surrounding state

A closure is a function that remembers the variables from the place where it was defined is a simpler way of saying that IMO which can be understood by everyone

Collapse
 
jonrandy profile image
Jon Randy πŸŽ–οΈ

Not really...

If that definition were correct, there would be no point having separate words for closure and function, since every function has access to its surrounding scope in this way.

Thread Thread
 
royaljain profile image
Royal Jain

Remembers is the keyword here

Every function has access but not every function remembers

Thread Thread
 
jonrandy profile image
Jon Randy πŸŽ–οΈ • Edited

They do. All functions are the same in this respect.

From MDN:

In JavaScript, closures are created every time a function is created, at function creation time.

Every function has an associated closure, remembering the lexical environment in which it was created.

Thread Thread
 
royaljain profile image
Royal Jain

No, they don’t. They access the variables again which are in their lexical scope. In Closure, they store the variables and their values. Which is what your diagram signifies - function + state

To check this - just define a global variable console log in a function change the value and try again. New value will be printed meaning it doesn’t remember it accesses.

Thread Thread
 
jonrandy profile image
Jon Randy πŸŽ–οΈ • Edited

Every function forms a closure with the lexical scope in which it was created. If a variable changes in that lexical scope, it will see the changed value. Make a JS module, export a function that accesses a module level var - this, again, works due to closures. Change the module level var, the change will be reflected. Often when code takes advantage of closures, it does so within the lexical scope of another function, which it still has access to when that function finishes execution. Closures work the same everywhere.

Your example with the global scope just reinforces the point. The global scope is still active and accessible, so vars there can be easily changed.

Variables are not 'stored' in a closure, a closure has references to the function's lexical scope.