How would you explain JavaScript closures to someone with a knowledge of the concepts they consist of (for example functions, variables and the like), but does not understand closures themselves?
I have seen the Scheme example given on Wikipedia, but unfortunately it did not help.
A closure is a pairing of:
A lexical environment is part of every execution context (stack frame) and is a map between identifiers (i.e. local variable names) and values.
Every function in JavaScript maintains a reference to its outer lexical environment. This reference is used to configure the execution context created when a function is invoked. This reference enables code inside the function to "see" variables declared outside the function, regardless of when and where the function is called.
If a function was called by a function, which in turn was called by another function, then a chain of references to outer lexical environments is created. This chain is called the scope chain.
In the following code,
innerforms a closure with the lexical environment of the execution context created whenfoois invoked, closing over variablesecret:In other words: in JavaScript, functions carry a reference to a private "box of state", to which only they (and any other functions declared within the same lexical environment) have access. This box of the state is invisible to the caller of the function, delivering an excellent mechanism for data-hiding and encapsulation.
And remember: functions in JavaScript can be passed around like variables (first-class functions), meaning these pairings of functionality and state can be passed around your program, similar to how you might pass an instance of a class around in C++.
If JavaScript did not have closures, then more states would have to be passed between functions explicitly, making parameter lists longer and code noisier.
So, if you want a function to always have access to a private piece of state, you can use a closure.
…and frequently we do want to associate the state with a function. For example, in Java or C++, when you add a private instance variable and a method to a class, you are associating the state with functionality.
In C and most other common languages, after a function returns, all the local variables are no longer accessible because the stack-frame is destroyed. In JavaScript, if you declare a function within another function, then the local variables of the outer function can remain accessible after returning from it. In this way, in the code above,
secretremains available to the function objectinner, after it has been returned fromfoo.Uses of Closures
Closures are useful whenever you need a private state associated with a function. This is a very common scenario – and remember: JavaScript did not have a class syntax until 2015, and it still does not have a private field syntax. Closures meet this need.
Private Instance Variables
In the following code, the function
toStringcloses over the details of the car.Functional Programming
In the following code, the function
innercloses over bothfnandargs.Event-Oriented Programming
In the following code, function
onClickcloses over variableBACKGROUND_COLOR.Modularization
In the following example, all the implementation details are hidden inside an immediately executed function expression. The functions
tickandtoStringclose over the private state and functions they need to complete their work. Closures have enabled us to modularize and encapsulate our code.Examples
Example 1
This example shows that the local variables are not copied in the closure: the closure maintains a reference to the original variables themselves. It is as though the stack-frame stays alive in memory even after the outer function exits.
Example 2
In the following code, three methods
log,increment, andupdateall close over the same lexical environment.And every time
createObjectis called, a new execution context (stack frame) is created and a completely new variablex, and a new set of functions (logetc.) are created, that close over this new variable.Example 3
If you are using variables declared using
var, be careful you understand which variable you are closing over. Variables declared usingvarare hoisted. This is much less of a problem in modern JavaScript due to the introduction ofletandconst.In the following code, each time around the loop, a new function
inneris created, which closes overi. But becausevar iis hoisted outside the loop, all of these inner functions close over the same variable, meaning that the final value ofi(3) is printed, three times.Final points:
functionfrom inside another function is the classic example of closure, because the state inside the outer function is implicitly available to the returned inner function, even after the outer function has completed execution.eval()inside a function, a closure is used. The text youevalcan reference local variables of the function, and in the non-strict mode, you can even create new local variables by usingeval('var foo = …').new Function(…)(the Function constructor) inside a function, it does not close over its lexical environment: it closes over the global context instead. The new function cannot reference the local variables of the outer function.Links