If you are a JavaScript developer, you may have heard of “closure.” Function closures are a powerful feature of JavaScript that can be used to create private variables, implement modules, and perform other advanced programming techniques. This article will dive deep into what function closures are, how they work, and how to use them effectively in your JavaScript code.
What is a Closure?
In simple terms, a closure is a function that has access to variables in its outer scope, even after the outer function has returned. When a function is defined inside another function, the inner function has access to all the variables and parameters of the outer function, even after the outer function has finished executing.
Here’s an example of a closure in action:
function outerFunction() {
const message = "Hello, World!";
function innerFunction() {
console.log(message);
}
return innerFunction;
}
const inner = outerFunction();
inner(); // "Hello, World!"
In this example, the outerFunction
returns the innerFunction
. When we call outerFunction
, it creates a variable message
and a function innerFunction
. The innerFunction
has access to the message
variable, even though it’s defined in the outer scope. When we call outerFunction
and assign its result to inner
, we get back the innerFunction
. Finally, when we call inner
, it logs the value of the message
variable, which is “Hello, World!”.
How Closures Work
Closures work by keeping a reference to the outer scope, even after the outer function has returned. In the example above, when outerFunction
is called, it creates a new execution context with its own set of variables and functions. When innerFunction
is defined, it has a reference to the outer scope, which includes the message
variable. This reference is called a “closure”.
When outerFunction
returns innerFunction
, it creates a new reference to the closure, which is stored in the inner
variable. This reference allows innerFunction
to access the message
variable, even though outerFunction
has already finished executing.
This is possible because JavaScript uses a mechanism called “garbage collection” to keep track of all the references to variables and functions. When a variable or function is no longer referenced by any other part of the program, it is considered “garbage” and can be safely removed from memory.
Benefits of Using Closures
There are several benefits to using closures in your JavaScript code:
Private Variables
One of the most powerful uses of closures is to create private variables. In JavaScript, there is no way to define a truly private variable – everything is accessible from the global scope. However, by using a closure, you can create a variable that is only accessible within a specific function.
function counter() {
let count = 0;
return function() {
count++;
console.log(count);
}
}
const increment = counter();
increment(); // 1
increment(); // 2
increment(); // 3

In this example, the counter
function returns a new function that increments a private count
variable and logs its value. Every time we call increment
, it accesses the same count
variable, which is stored in the closure. This allows us to create a private variable that is only accessible from within the increment
function.
Caching
Another benefit of closures is that they can be used to cache expensive function calls. Suppose you have a function that takes a long time to execute, and you want to avoid running it multiple times with the same input. You can use a closure to store the result of the function call, and return that result the next time the function is called with the same input.
function expensiveOperation() {
// ...
}
function cache() {
const results = {};
return function(input) {
if (input in results) {
return results[input];
}
const result = expensiveOperation(input);
results[input] = result;
return result;
}
}
const cachedOperation = cache();
cachedOperation("input1"); // performs expensive operation
cachedOperation("input1"); // returns cached result
In this example, the cache
function returns a new function that takes an input and caches the result of an expensive operation. Every time we call the cached function with a new input, it checks if the input has been cached already. If it has, it returns the cached result. Otherwise, it performs the expensive operation, caches the result, and returns it.
Modules
Closures can also be used to implement modules – self-contained units of code that expose a public interface while hiding their implementation details. By using a closure to store private variables and functions, you can create a module that encapsulates its state and behavior, and only exposes what is necessary to the outside world.
const counter = (function() {
let count = 0;
function increment() {
count++;
}
function decrement() {
count--;
}
function getCount() {
return count;
}
return {
increment,
decrement,
getCount
}
})();
counter.increment();
counter.increment();
counter.decrement();
console.log(counter.getCount()); // 1
In this example, we define a new function that returns an object with three methods – increment
, decrement
, and getCount
. These methods have access to a private count
variable, which is stored in the closure. By returning only the public methods, we create a module that exposes a public interface while hiding its internal state.
Common Mistakes
While closures are a powerful feature of JavaScript, they can also be a source of confusion and bugs if not used correctly. Here are some common mistakes to watch out for when using closures:
Creating Multiple Closures
When you define a function inside another function, a new closure is created every time the outer function is called. If you define multiple functions inside the outer function, each function will have its own closure, with its own set of private variables. This can lead to unexpected behavior if you’re not careful.
function outerFunction() {
const messages = ["Hello", "World"];
const functions = [];
for (let i = 0; i < messages.length; i++) {
functions.push(function() {
console.log(messages[i]);
});
}
return functions;
}
const innerFunctions = outerFunction();
for (const innerFunction of innerFunctions) {
innerFunction(); // undefined
}
In this example, the outerFunction
creates an array of messages and an array of functions that log each message. However, because each function has its own closure, they all reference the same i
variable, which is incremented in the for loop. This means that when we call the inner functions, they all log undefined
, because i
is now equal to messages.length
.
To fix this, we can create a new closure for each function by wrapping it in an immediately-invoked function expression (IIFE):
function outerFunction() {
const messages = ["Hello", "World"];
const functions = [];
for (let i = 0; i < messages.length; i++) {
functions.push((function(message) {
return function() {
console.log(message);
})(messages[i]));
}
return functions;
}
const innerFunctions = outerFunction();
for (const innerFunction of innerFunctions) {
innerFunction(); // "Hello", "World"
}
In this updated example, we create a new closure for each function by passing the `message` variable to an immediately-invoked function expression. This way, each function has its own reference to the `message` variable, and we avoid the problem of multiple closures with shared state.
Forgetting to Return a Value
When you define a function inside another function, and you want to return the inner function to use later, you need to make sure that you actually return the inner function, and not just call it.
function outerFunction() {
const message = "Hello, World!";
function innerFunction() {
console.log(message);
}
// Wrong way - returns undefined
return innerFunction();
// Right way - returns the function
// return innerFunction;
}
const inner = outerFunction();
inner(); // TypeError: inner is not a function
In this example, the `outerFunction` defines an `innerFunction` that logs a message. However, when we return the `innerFunction`, we accidentally call it instead of returning it. This means that the `inner` variable is set to `undefined`, and we get a `TypeError` when we try to call it.
To fix this, we simply need to return the `innerFunction` without calling it:
function outerFunction() {
const message = "Hello, World!";
function innerFunction() {
console.log(message);
}
return innerFunction;
}
const inner = outerFunction();
inner(); // "Hello, World!"
In this updated example, we return the `innerFunction` without calling it. This means that the `inner` variable is set to a function, which we can then call to log the message.
Conclusion
Function closures are a powerful feature of JavaScript that can be used to create private variables, implement modules, and perform other advanced programming techniques. By understanding how closures work and how to use them effectively, you can write more concise, readable, and maintainable JavaScript code.
Remember to watch out for common mistakes like creating multiple closures with shared state and forgetting to return a value, and always test your code thoroughly to ensure that it behaves as expected.
With this knowledge, you can take advantage of closures to write more powerful and expressive JavaScript code that is easier to understand and maintain.
📕 Related articles about Javascript
- The Power of JavaScript Switch Statements: A Comprehensive Guide
- JavaScript Numbers
- JavaScript Loop While
- Understanding JavaScript Objects
- Understanding JavaScript Array Const
- JavaScript Function Definitions