The Ultimate Guide to Closures in JavaScript.
Introduction
Closures are a fundamental concept in JavaScript that can be a bit tricky to grasp at first, but once understood, they unlock a powerful feature of the language. In this blog, we'll dive deep into closures, exploring what they are, how they work, and why they're so important.
What is closure
A closure is a function that retains access to its lexical scope, even when the function is executed outside that scope. This means that a function defined inside another function can "remember" the variables from the outer function, even after the outer function has finished executing.
When a function is declared, it has access to variables within its own scope, as well as variables from its parent scope. A closure occurs when a function "remembers" these variables from its parent scope even after the parent function has finished executing.
"Function along with its lexical scope bundled together forms a closure, Closures are created every time a function is created."
function outerFunction() {
let outerVariable = 'I am outside!';
function innerFunction() {
console.log(outerVariable);
}
return innerFunction;
}
const myClosure = outerFunction();
myClosure(); // Output: I am outside!
In this example, outerFunction
creates a variable outerVariable
and defines an innerFunction
that logs outerVariable
to the console. The innerFunction
is returned and assigned to the variable myClosure
. When myClosure
is called, it still has access to outerVariable
even though outerFunction
has finished executing. This is a closure in action.
Uses of closures
Data Privacy & Hiding: Closures can be used to create private variables. Variables within a closure cannot be accessed from outside the function, which is useful for maintaining data encapsulation.
function createCounter() {
let count = 0;
return function() {
count++;
return count;
};
}
const counter = createCounter();
console.log(counter()); // Output: 1
console.log(counter()); // Output: 2
Currying: Closures allow us to create functions with preset parameters, which can be useful for functional programming techniques.
function multiply(a) {
return function(b) {
return a * b;
};
}
const multiplyByTwo = multiply(2);
console.log(multiplyByTwo(5)); // Output: 10
Some other use cases of closures are Module Design pattern, Memoization, Maintain state in async world & setTimeout
Closures in Loops
A common pitfall for beginners is using closures within loops. Consider the following example:
for (var i = 1; i <= 5; i++) {
setTimeout(function() {
console.log(i);
}, i * 1000);
}
You might expect this to log numbers 1 to 5 with a delay, but instead, it logs the number 6 five times. This happens because the setTimeout
function forms a closure that references the variable i
, which is incremented to 6 by the time the callbacks are executed.
To fix this, you can use let
instead of var
:
for (let i = 1; i <= 5; i++) {
setTimeout(function() {
console.log(i);
}, i * 1000);
}
With let
, each iteration of the loop creates a new block scope, and i
retains its value within each iteration, producing the expected output.
Common Drawbacks of Closures
While closures are powerful, they can sometimes lead to issues if not used carefully:
Memory Leaks: Closures can inadvertently cause memory leaks if they hold references to variables that are no longer needed.
Unintended Variable Sharing: When using closures in loops, variables can be unintentionally shared among all instances of the closure. This can be mitigated using let instead of var or by creating additional closures.
Over consumption of memory
Can freeze browsers if not handled properly.
The closed over variables are not garbage collected till the program expires
Garbage Collection: It is a process of automatic memory management, It removes objects, variables etc that are not in use anymore.
Few Important Points
Function Parameter: Function parameters are closed over as they are part of outer function
Relation of scope Chain & Closures: If outer function is present inside another function It’ll form a closure with that also.
Conflicting name with global variable: Javascript will take value to the first variable it encounters, It’ll check in the outer function scope then in outer & then in global scope. If variable is not found, it’ll throw an reference error
Conclusion
Closures are a fundamental and powerful feature of JavaScript that enable functions to retain access to their lexical scope. They are useful for creating private variables, implementing functional programming techniques, and managing asynchronous code. By understanding closures and their potential pitfalls, you can write more robust and maintainable JavaScript code.