DEV Community

Abimanyu P
Abimanyu P

Posted on

Closure in javascript

Closures in JavaScript

Closures are one of the most important concepts in JavaScript. They can look confusing at first because they involve functions, lexical scope, and lexical environments together. But once we understand how these concepts are connected, closures become much easier to understand.

A simple definition of closure is:

A closure is a function that remembers and can access variables from its surrounding lexical environment even after the outer function has finished executing.

The word "remembers" here doesn't mean that JavaScript literally copies the variables into the function. Instead, the function maintains a connection to the lexical environment in which it was created.

Let's understand it with an example

Consider the following code:

function outer() {
    let name = "Abimanyu"

    function inner() {
        console.log(name)
    }

    return inner
}

let myFunction = outer()

myFunction()
Enter fullscreen mode Exit fullscreen mode

When outer() is called, JavaScript creates a lexical environment for it. That environment contains the variable name:

Outer Lexical Environment
    name → "Abimanyu"
Enter fullscreen mode Exit fullscreen mode

The inner() function is created inside outer(), so it has access to that surrounding environment. When outer() returns inner, the function is stored in myFunction.

Now outer() has finished executing, but myFunction still refers to inner().

myFunction
    ↓
  inner()
    ↓
Outer Lexical Environment
    ↓
name → "Abimanyu"
Enter fullscreen mode Exit fullscreen mode

When we call:

myFunction()
Enter fullscreen mode Exit fullscreen mode

inner() needs the value of name. Since name is not inside its own environment, JavaScript looks through its surrounding environment and finds name in the environment created by outer().

This is the important part of a closure: the function retains access to the environment where it was created, even though the outer function has already finished executing.

Why doesn't name disappear?

This is where closures are often misunderstood.

You might think that once outer() finishes, everything created inside it should disappear. But inner() still has a reference to the environment containing name. Since that environment is still reachable through the function, JavaScript cannot simply remove it.

So conceptually, after outer() has finished, we can still think of the relationship like this:

myFunction
    ↓
inner()
    ↓
remembered lexical environment
    ↓
name → "Abimanyu"
Enter fullscreen mode Exit fullscreen mode

The function and its connection to the surrounding environment together form the idea of a closure.

The word "closure" therefore describes the behavior where a function closes over the variables available in its surrounding lexical scope and continues to access them later.

Closure is connected to lexical scope

To understand closures properly, it is useful to remember what lexical scope means.

Lexical scope means that the scope of a function is determined by where the function is written in the code, not where the function is called.

For example, inner() was written inside outer(), so its surrounding lexical scope includes outer().

function outer() {
    let message = "Hello"

    function inner() {
        console.log(message)
    }

    return inner
}
Enter fullscreen mode Exit fullscreen mode

Because of where inner() is written, it can access message.

This relationship exists before we even call the function. The function's position in the source code determines its lexical surroundings.

Closures can maintain state

One of the most useful things about closures is that they allow a function to maintain state between calls.

function createCounter() {
    let count = 0

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

let counter = createCounter()

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

Here, createCounter() finishes executing after returning the function. However, the returned function still has access to count.

Each time counter() is called, it accesses the same count variable and changes its value.

So the value isn't recreated from scratch on every call. The closure keeps the connection to the environment where count was created.

This ability to preserve state is one reason closures are so useful in JavaScript.

Closures can also provide data privacy

Closures can be used to keep certain variables inaccessible directly from outside code.

For example:

function createAccount() {
    let balance = 1000

    return {
        deposit(amount) {
            balance += amount
        },

        getBalance() {
            return balance
        }
    }
}

let account = createAccount()

account.deposit(500)

console.log(account.getBalance()) // 1500
Enter fullscreen mode Exit fullscreen mode

Here, balance cannot be accessed directly through account.balance. However, deposit() and getBalance() can access it because both functions were created inside the environment containing balance.

The closure therefore allows those functions to work with the private state without exposing the variable directly.

Top comments (0)