Skip to main content

What is Closure in JavaScript

Closures are created whenever a function is defined inside another function.

The inner function has access to the outer function's variables and parameters, even after the outer function has returned.

This is because the inner function has formed a closure over the outer function's variables.

Here's an example:

function outerFunction() {
var outerVariable = "Hello";

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

return innerFunction;
}

var closure = outerFunction();
closure(); // logs 'Hello'

In this example:

  • OuterFunction returns innerFunction.
  • InnerFunction has access to outerVariable even though outerFunction has returned, because innerFunction has formed a closure over outerVariable.

Uses of JavaScript Closures

Closures are often used in JavaScript for a variety of purposes, Such as:

Private variables and functions

Closures can be used to create private variables and functions that are not accessible from outside the function that defines them.

This is useful for creating modules and libraries that have internal state and behavior that is not visible to the rest of the program.

As an example:

function counter() {
var count = 0;

function increment() {
count++;
console.log(count);
}

return increment;
}

var counter1 = counter();
counter1(); // logs 1
counter1(); // logs 2

In this example:

  • The counter() function creates a private count variable and a increment() function that increments the count and logs its value.
  • counter() returns the increment() function, which is assigned to counter1. When counter1() is called, it logs and increments the count, and the private count variable persists between function calls.

Callbacks

Closures are often used to create callbacks that retain access to variables from the calling function.

This is useful for asynchronous programming, where a callback may need access to state that is not available when it is called.

As an example:

function doAsyncTask(callback) {
var data = "some data";
setTimeout(function () {
callback(data);
}, 1000);
}

doAsyncTask(function (result) {
console.log(result);
});

In this example:

  • The doAsyncTask() function takes a callback function as an argument and asynchronously calls it with some data after a delay.
  • The callback retains access to the data variable through a closure, even though the doAsyncTask() function has completed execution.

Memoization

Closures can be used to implement memoization, which is a technique for caching the results of expensive function calls.

Memoization is useful for functions that are called frequently with the same inputs, as it can significantly improve performance.

As an example:

function memoize(func) {
var cache = {};

return function () {
var key = JSON.stringify(arguments);
if (cache[key]) {
return cache[key];
} else {
var result = func.apply(null, arguments);
cache[key] = result;
return result;
}
};
}

function fibonacci(n) {
if (n <= 1) return 1;
return fibonacci(n - 1) + fibonacci(n - 2);
}

var memoizedFibonacci = memoize(fibonacci);
console.log(memoizedFibonacci(10)); // logs 89

In this example:

  • The memoize() function takes a function as an argument and returns a memoized version of it that caches the results of previous function calls.
  • The cache is stored in a closure, allowing it to persist between function calls.
  • The fibonacci() function is used as an example of a function that can benefit from memoization, as it is called frequently with the same inputs.
  • The memoized version of fibonacci() is created using memoize(fibonacci), and is then called with memoizedFibonacci(10), which logs the result of the Fibonacci sequence at position 10 (which is 89).