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
returnsinnerFunction
.InnerFunction
has access toouterVariable
even thoughouterFunction
has returned, becauseinnerFunction
has formed a closure overouterVariable
.
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 aincrement()
function that increments the count and logs its value. counter()
returns theincrement()
function, which is assigned to counter1. Whencounter1()
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 withmemoizedFibonacci(10)
, which logs the result of the Fibonacci sequence at position 10 (which is 89).