[Javascript] Closure

What is a Closure?

In simple terms, an inner function captures the context of the outer function. But, what is a context?

The context refers to the environment in which a function is declared. This environment includes any variables, parameters, and functions that are within the scope of the function when it is defined.

When we say that an inner function “captures the context” of the outer function, we mean that the inner function retains access to the variables and parameters of the outer function, even after the outer function has completed execution. This capability is a core aspect of closures in many languages.

Let’s see an example.

function outerFunction(outerVariable) {
    let outerLocalVariable = 'I am local to the outer function';

    function innerFunction(innerVariable) {
        console.log(outerVariable); // Accessing the parameter of the outer function
        console.log(outerLocalVariable); // Accessing the local variable of the outer function
        console.log(innerVariable); // Accessing the parameter of the inner function
    }

    return innerFunction;
}

const myInnerFunction = outerFunction('I am passed to the outer function');
myInnerFunction('I am passed to the inner function');

If you executes the code above in the browser, you will see the result below.

outerVariable and outerLocalVariable are part of the context of outerFunction.

The innerfunction captures this context.

If you are interested in Golang…

This is more clear and complex example written in Golang. If you are interested in Golang, let’s take a look.

package main

import (
    "fmt"
    "time"
)

func createWorker(id int) func() {
    counter := 0
    return func() {
        counter++
        fmt.Printf("Worker %d: %d\n", id, counter)
    }
}

func main() {
    const numWorkers = 3
    var workers [numWorkers]func()

    for i := 0; i < numWorkers; i++ {
        workers[i] = createWorker(i)
    }

    for i := 0; i < numWorkers; i++ {
        go func(worker func()) {
            for j := 0; j < 5; j++ {
                worker()
                time.Sleep(time.Millisecond * 100)
            }
        }(workers[i])
    }

    time.Sleep(time.Second)
}

As the createWorker function is called, the counter variable in the context of createWorker is captured by the closure.

When the closure is passed to the go routine, the captured variable counter can be still used in another context.

In summary, a closure is a function that captures the context of its outer function, allowing it to access variables from that outer function’s scope even when executing in a different context.

Why Closures?

  • Encapsulation: We can create private variables that are not accessible from outside the function. This feature can be used to create modular code and avoiding global variables.

  • Callback and Asynchronous Programming: In environment like Javascript, closures are essential for handling call backs and asychronous programming. They ensure that the callbakc has access to the necessary context when it is eventually executed.

  • Higher-order Functions: this allows for more abstract and flexible code. For example, we can create a function factory that generates functions based on input parameters. See the example below.
function createMultiplier(multiplier) {
    return function(value) {
        return value * multiplier;
    };
}

const double = createMultiplier(2);
const triple = createMultiplier(3);

console.log(double(5)); // 10
console.log(triple(5)); // 15

Conclusion

In summary, closures are a tool that allow for more modular, maintainable, and expressive code. They provide mechanism for encapsulation, maintaining, and creating high-order functions, making them indispensable.

Leave a Reply

Your email address will not be published. Required fields are marked *