I recently purchased and read the book You Don't Know JS Yet by Kyle Simpson, which I found to be a great read. Even though I've worked as a JS developer for years, there were so many new things I learned by reading it (I am not endorsed by anyone affiliated with this book - I'm just recommending it because I genuinely liked it).
One of those things was "closure" - a term I had heard a couple of times but never understood what it actually meant. I think it's hard to beat Kyle Simpson's definition:
Closure is when a function remembers and continues to access variables from outside its scope, even when the function is executed in a different scope.
So, what does that look like?
Some Examples of Closure
You've probably already used closure before and just didn't realize it. Take the following example:
function doAsyncTask(successMsg) {
someAsyncTask().then(() => {
console.log(`I remembered your variable! ${successMsg}`);
});
}
getSuperImporantInfo('Hooray!');
// Some time later...
// I remembered your variable! Hooray!
When someAsyncTask
finishes executing, it prints out the successMsg
variable passed to doAsyncTask
. someAsyncTask
could take several seconds or even several minutes to execute, but the callback function passed to then
"remembers" the successMsg
variable. We say the the callback function is "closed" over successMsg
.
I've done things like this all the time, I just didn't know I was using closure!
Now let's say you want to create a counter function. Every time you call the function, it will return the next number after the last number it returned. You can use closure to "remember" the last number returned.
function createCounter() {
let count = 0;
return () => count++;
}
const inc = createCounter();
inc();
// 0
inc();
// 1
inc();
// 2
createCounter
returns an anonymous function which has access to the count
variable. The function returned by createCounter
is "closed" over count
. We can even create multiple increment functions, all of which will have their own copy of count
.
const inc1 = createCounter();
const inc2 = createCounter();
inc1();
// 0
inc2();
// 0
inc1();
// 1
inc1();
// 2
inc2();
// 1
These may be simple examples, but I've certainly needed to write counter functions like this before. Before I knew about closure, I would create variables visible to my entire module and increment those in my counter function. Now I know there's a better way that doesn't require me polluting my modules' scopes.
That's all there is to it! What other examples can you think of where you could use closure to your advantage?
Top comments (3)
This is somewhat incorrect.
A lexical closure can only form over variables in scope at that point.
a is in scope in the body of foo, but it is not a bound variable of the function foo -- it is a free variable.
A function with a free variable is an open expression, and can't be understood on its own.
To close the function, we capture (and share) the free variable, which binds it, and call the result a closure.
It also doesn't really make sense to talk about executing a function in a scope -- scope is a lexical property of the program, not a dynamic property of execution.
The point of producing a closed function is that it doesn't execute in a scope -- it has all of the things it requires bound already, so it just executes in the global environment like any other function.
You could pass foo to any other function and it would still return
1
, even whena
goes out of scope; how is that not a closure?Even when a goes out of the scope of what? :)