In JavaScript, when a function is invoked, it creates a temporary storage space called the local memory
(also known as the variable environment or state) for that execution context. This local memory holds all the data needed while the function runs. Typically, once the function completes its execution, this local memory is deleted, except for any value that the function returns.
What if Functions Could Remember?
Imagine if functions could retain this live data between executions. This ability would allow function definitions to have an associated cache or persistent memory. The concept becomes clearer when we think about returning a function from another function.
Defining Functions Within Functions
The place where you define your function determines which variables the function can access when it’s called. Let’s look at an example to understand this better:
function outer() {
let counter = 0;
function incrementCounter() {
counter++;
console.log(counter);
}
return incrementCounter;
}
const myNewFunc = outer();
myNewFunc(); // Outputs: 1
myNewFunc(); // Outputs: 2
const myNewFuncII = outer();
myNewFuncII(); // Outputs: 1
myNewFuncII(); // Outputs: 2
In the above code, myNewFunc
contains the incrementCounter
function. When executed, myNewFunc
first looks for the counter
variable in its local memory. If not found, it then accesses a backpack of data that came bundled with the function definition. This backpack
is what we call a closure.
How Closures Work
When a function is returned from another function, it is returned along with its closure. This closure acts as a permanent memory for the function, persisting through subsequent function calls. The closure contains all the variables used by the function. Variables that are not used inside the function are not included in its closure, thanks to an optimization by the JavaScript engine, which assesses the function’s needs by examining its definition.
Practical Implications of Closures
Closures allow functions to have persistent memories. By understanding closures, developers unlock a new toolkit for writing efficient and effective code. Closures are particularly useful in:
- Helper functions
- Iterators and generators
- The module pattern
- Asynchronous JavaScript
Key Takeaway
A key thing to remember is that when a function is declared, it contains not just the function definition but also a closure. This closure is a collection of all the variables in scope at the time of the function’s creation, acting as a persistent memory that enhances the function’s capability to manage data over time.
A good read on closures.