This provides the ability to access an instance of a function and it’s scope (and so access it’s scoped variables and functions that were available at the time of creation) from outside of the original scope.
Or in the words of the Mozilla site, “A closure is the combination of a function and the lexical environment within which that function was declared. This environment consists of any local variables that were in-scope at the time the closure was created.”
If this all just sounds like words, let’s see some code.
doSomething function takes an argument as parameter
doSomething we create an
increment function that takes an argument as parameter
increment function adds
b, assigns it to
a and outputs it to the console.
We then return the
increment function from
myNumber is assigned the return value of
doSomething, which is passed the argument
1. So within
a = 1.
myNumber now references
increment. When we invoke
myNumber(1)) with an argument of 1, we actually invoke
increment with an argument of 1 which adds the value of
a (1) to
b (1) and outputs 2 to the console.
If you invoke
myNumber again, the output is 3, because the value of
a that’s available to the scope of
increment is 2 from the previous execution. This is what is meant by the
increment function having access to the original lexical scope, but accessed from outside using
increment function has closure over the
doSomething function. It’s like an instance of scope where the variables, functions and values are retained for that scope!
This becomes even clearer when you see that the
const anotherNumber has access to it’s own value for
a, independent of the
myNumber assignment. When
anotherNumber(10) invokes the
increment function, the value of
a within scope is 10 from the
const anotherNumber = doSomething(10) assignment.
Invoke these references to
increment separately again (
myNumber(1); anotherNumber(10);) and the outputs are relative to the corresponding values of
a for each scope.
Why Do I Need to Know About Closures?
Understanding closures also helps you understand what’s available in scope when code executes, and certain gotchas.
Take this for example:
We’ve added an event listener to two elements in the HTML, one with an id of
el-0 and one with an id of
el-1. On click of those HTML elements, we output the value of
i from the
for loop to the console.
When you click either of those HTML elements, you see the value
2, rather than the corresponding
1 for each iteration of the loop. This is because by the time the click occurs
i has a value of
2 from the end of the
for loop, as the
for loop doesn’t create a closure within it’s curly braces.
To amend this to show the respective
1 for each element, we can create a closure. Creating the closure means, as we’ve described, the reference to lexical scope is retained and still available for access even after the
for loop has moved on with updated values for
We can create a closure by using an immediately invoked anonymous function (IIFE).
If you’re not sure what an IIFE is, have a quick look at this article, but essentially it’s a function that’s called immediately. Passed into that function is the argument
i, which is received as parameter
i. So within each
for loop iteration the
function is called, and the IIFE creates a closure, retaining the value of
i passed in to the function in the parameter
j (it could have been called
i also, but
j here differentiates between the
i that’s passed in, and the
j that receives it).
This means we now click the HTML element and the scope lookup for the
console.log() can look to it’s parent scope where the
j reference has been retained by the nature of the closure. We now get
0 when we click the element with id
1 when we click the element with id
This isn’t a hugely practical example, but shows the availability of scope with and without closures.
There’s a magical nature of
for loops in that they create a closure for the loop.
So rather than having to create the IIFE closure above, the
let creates closure, retaining the value of
i for each iteration of the loop, making it accessible later when we click the element. Pretty cool! 😎🍻
If you have any questions or comments or want to connect, you can follow me on Twitter, or sign up to the newsletter in the footer for front-end articles to your inbox 👇