DEV Community

Cover image for Understanding Closures in JavaScript
Shounak Das
Shounak Das

Posted on • Originally published at shounak.hashnode.dev

Understanding Closures in JavaScript

The callbacks, event handlers, higher-order functions can access outer scope variables thanks to closures. They are important in functional programming and are often asked in coding interviews.

Closures are difficult to grasp at the beginning. If you haven't reached the "Aha!" moment in understanding closures, I hope this article will benefit you.

Scope

In order to understand closures, you must have a very good understanding of scopes.

The scope is the context in which values and expressions are visible or can be referenced.

There are 3 types of variable scopes:

  1. Global scope

  2. Local scope

  3. Block scope (introduced in ES6)

Global Variables

Any variable that is declared outside a function has a global scope.

A global variable can be accessed by all functions and scripts in a document.

var fruit = "mango";
// fruit can be accessed here

function myFunc() {
    // fruit can be accessed here as well
    alert(fruit);
}
Enter fullscreen mode Exit fullscreen mode

Also, any variable that is initialized or has been assigned a value inside a function without being declared first will be global.

myFunc(); // call the function first

document.getElementByID("fruit").innerHTML = "My favorite fruit is " + fruit;
// fruit can be accessed here

function myFunc() {
    fruit = "mangoes";
    alert(fruit);
    // fruit can be used here too
}
Enter fullscreen mode Exit fullscreen mode

Local Variables

Any variable that is declared inside a function has a local scope.

A local variable can be only accessed by the function where it was declared.

// fruit can NOT be used here
document.getElementByID("fruit").innerHTML = "My favorite fruit is " + fruit;

function myFunc() {
    var fruit = "mangoes";
    alert(fruit);
    // fruit can be used here
}
Enter fullscreen mode Exit fullscreen mode

Note:- Changes made to a variable in the global scope takes effect inside the local scope as well

However, changes made inside the local scope don't affect the global scope.

Similar to the local scope is the Block scope.

A block is any part of the code enclosed within { and }.

Variables declared using the let keyword are accessible within the block in which it is declared.

Note:- Variables declared with the var keyword can NOT have block scope.

Scope Nesting

Let’s play a bit more with scopes, and nest one scope into another. For example, the function innerFunc() is nested inside an outer function outerFunc().

function outerFunc() {
    // Outer scope
    let outerVar = "I'm outside";

    function innerFunc() {
        // Inner scope
        console.log(outerVar);
    }

    innerFunc();
}
Enter fullscreen mode Exit fullscreen mode

Can you access the variable outerVar of outerFunc() from within innerFunc() scope?

Indeed. Try it yourself and find out.

Now you know two interesting things:

  1. Scopes can be nested.

  2. The variables of the outer scope are accessible inside the inner scope.

The Lexical Scope

How does JavaScript understand that outerVar inside innerFunc() corresponds to the variable outerVar of outerFunc()?

JavaScript implements a scoping mechanism named lexical scoping (or static scoping).

Lexical scoping means that the accessibility of variables is determined by the position of the variables inside the nested scopes.

It’s called lexical (or static) because the engine determines the nesting of scopes at the lexing (aka tokenizing) time just by looking at the JavaScript source code, without executing it.

The Closure

Let’s make a change: innerFunc() to be invoked outside of its lexical scope (outside of outerFunc()).

Would innerFunc() still be able to access outerVar? Let's see.

function outerFunc() {
    let outerVar = "I'm outside";

    function innerFunc() {
        console.log(outerVar); // logs "I'm outside"
    }

    return innerFunc();
}

const myInnerFunc = outerFunc();
myInnerFunc();
Enter fullscreen mode Exit fullscreen mode

innerFunc() still has access to outerVar from its lexical scope, even after being executed outside of its lexical scope.

In other words, innerFunc() remembers the variable outerVar from its lexical scope.

So, innerFunc() is a closure because it closes over the variable outerVar from its lexical scope.

The closure is a function that has access to its lexical scope when executed outside of its lexical scope.

A rule of thumb to identify a closure:

If inside a function you see a variable not defined inside that function, most likely that function is a closure because that particular variable is captured.

Thanks for reading 🙏

More articles on JavaScript soon.


I write daily Twitter threads on web development concepts, resources, and tips.

Discussion (0)