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.
Closures are simple:
The following simple example covers all the main points of JavaScript closures.*
Here is a factory that produces calculators that can add and multiply:
The key point: Each call to
make_calculator
creates a new local variablen
, which continues to be usable by that calculator'sadd
andmultiply
functions long aftermake_calculator
returns.If you are familiar with stack frames, these calculators seem strange: How can they keep accessing
n
aftermake_calculator
returns? The answer is to imagine that JavaScript doesn't use "stack frames", but instead uses "heap frames", which can persist after the function call that made them returns.Inner functions like
add
andmultiply
, which access variables declared in an outer function**, are called closures.That is pretty much all there is to closures.
* For example, it covers all the points in the "Closures for Dummies" article given in another answer, except example 6, which simply shows that variables can be used before they are declared, a nice fact to know but completely unrelated to closures. It also covers all the points in the accepted answer, except for the points (1) that functions copy their arguments into local variables (the named function arguments), and (2) that copying numbers creates a new number, but copying an object reference gives you another reference to the same object. These are also good to know but again completely unrelated to closures. It is also very similar to the example in this answer but a bit shorter and less abstract. It does not cover the point of this answer or this comment, which is that JavaScript makes it difficult to plug the current value of a loop variable into your inner function: The "plugging in" step can only be done with a helper function that encloses your inner function and is invoked on each loop iteration. (Strictly speaking, the inner function accesses the helper function's copy of the variable, rather than having anything plugged in.) Again, very useful when creating closures, but not part of what a closure is or how it works. There is additional confusion due to closures working differently in functional languages like ML, where variables are bound to values rather than to storage space, providing a constant stream of people who understand closures in a way (namely the "plugging in" way) that is simply incorrect for JavaScript, where variables are always bound to storage space, and never to values.
** Any outer function, if several are nested, or even in the global context, as this answer points out clearly.
A closure is where an inner function has access to variables in its outer function. That's probably the simplest one-line explanation you can get for closures.
The author of Closures has explained closures pretty well, explaining the reason why we need them and also explaining LexicalEnvironment which is necessary to understanding closures.
Here is the summary:
What if a variable is accessed, but it isn’t local? Like here:
In this case, the interpreter finds the variable in the outer
LexicalEnvironment
object.The process consists of two steps:
When a function is created, it gets a hidden property, named [[Scope]], which references the current LexicalEnvironment.
If a variable is read, but can not be found anywhere, an error is generated.
Nested functions
Functions can be nested one inside another, forming a chain of LexicalEnvironments which can also be called a scope chain.
So, function g has access to g, a and f.
Closures
A nested function may continue to live after the outer function has finished:
Marking up LexicalEnvironments:
As we see,
this.say
is a property in the user object, so it continues to live after User completed.And if you remember, when
this.say
is created, it (as every function) gets an internal referencethis.say.[[Scope]]
to the current LexicalEnvironment. So, the LexicalEnvironment of the current User execution stays in memory. All variables of User also are its properties, so they are also carefully kept, not junked as usually.The whole point is to ensure that if the inner function wants to access an outer variable in the future, it is able to do so.
To summarize:
This is called a closure.
Whenever you see the function keyword within another function, the inner function has access to variables in the outer function.
This will always log 16, because
bar
can access thex
which was defined as an argument tofoo
, and it can also accesstmp
fromfoo
.That is a closure. A function doesn't have to return in order to be called a closure. Simply accessing variables outside of your immediate lexical scope creates a closure.
The above function will also log 16, because
bar
can still refer tox
andtmp
, even though it is no longer directly inside the scope.However, since
tmp
is still hanging around insidebar
's closure, it is also being incremented. It will be incremented each time you callbar
.The simplest example of a closure is this:
When a JavaScript function is invoked, a new execution context is created. Together with the function arguments and the parent object, this execution context also receives all the variables declared outside of it (in the above example, both 'a' and 'b').
It is possible to create more than one closure function, either by returning a list of them or by setting them to global variables. All of these will refer to the same
x
and the sametmp
, they don't make their own copies.Here the number
x
is a literal number. As with other literals in JavaScript, whenfoo
is called, the numberx
is copied intofoo
as its argumentx
.On the other hand, JavaScript always uses references when dealing with objects. If say, you called
foo
with an object, the closure it returns will reference that original object!As expected, each call to
bar(10)
will incrementx.memb
. What might not be expected, is thatx
is simply referring to the same object as theage
variable! After a couple of calls tobar
,age.memb
will be 2! This referencing is the basis for memory leaks with HTML objects.JavaScript functions can access their:
If a function accesses its environment, then the function is a closure.
Note that outer functions are not required, though they do offer benefits I don't discuss here. By accessing data in its environment, a closure keeps that data alive. In the subcase of outer/inner functions, an outer function can create local data and eventually exit, and yet, if any inner function(s) survive after the outer function exits, then the inner function(s) keep the outer function's local data alive.
Example of a closure that uses the global environment:
Imagine that the Stack Overflow Vote-Up and Vote-Down button events are implemented as closures, voteUp_click and voteDown_click, that have access to external variables isVotedUp and isVotedDown, which are defined globally. (For simplicity's sake, I am referring to StackOverflow's Question Vote buttons, not the array of Answer Vote buttons.)
When the user clicks the VoteUp button, the voteUp_click function checks whether isVotedDown == true to determine whether to vote up or merely cancel a down vote. Function voteUp_click is a closure because it is accessing its environment.
All four of these functions are closures as they all access their environment.
As a father of a 6-year-old, currently teaching young children (and a relative novice to coding with no formal education so corrections will be required), I think the lesson would stick best through hands-on play. If the 6-year-old is ready to understand what a closure is, then they are old enough to have a go themselves. I'd suggest pasting the code into jsfiddle.net, explaining a bit, and leaving them alone to concoct a unique song. The explanatory text below is probably more appropriate for a 10 year old.
INSTRUCTIONS
DATA: Data is a collection of facts. It can be numbers, words, measurements, observations or even just descriptions of things. You can't touch it, smell it or taste it. You can write it down, speak it and hear it. You could use it to create touch smell and taste using a computer. It can be made useful by a computer using code.
CODE: All the writing above is called code. It is written in JavaScript.
JAVASCRIPT: JavaScript is a language. Like English or French or Chinese are languages. There are lots of languages that are understood by computers and other electronic processors. For JavaScript to be understood by a computer it needs an interpreter. Imagine if a teacher who only speaks Russian comes to teach your class at school. When the teacher says "все садятся", the class would not understand. But luckily you have a Russian pupil in your class who tells everyone this means "everybody sit down" - so you all do. The class is like a computer and the Russian pupil is the interpreter. For JavaScript the most common interpreter is called a browser.
BROWSER: When you connect to the Internet on a computer, tablet or phone to visit a website, you use a browser. Examples you may know are Internet Explorer, Chrome, Firefox and Safari. The browser can understand JavaScript and tell the computer what it needs to do. The JavaScript instructions are called functions.
FUNCTION: A function in JavaScript is like a factory. It might be a little factory with only one machine inside. Or it might contain many other little factories, each with many machines doing different jobs. In a real life clothes factory you might have reams of cloth and bobbins of thread going in and T-shirts and jeans coming out. Our JavaScript factory only processes data, it can't sew, drill a hole or melt metal. In our JavaScript factory data goes in and data comes out.
All this data stuff sounds a bit boring, but it is really very cool; we might have a function that tells a robot what to make for dinner. Let's say I invite you and your friend to my house. You like chicken legs best, I like sausages, your friend always wants what you want and my friend does not eat meat.
I haven't got time to go shopping, so the function needs to know what we have in the fridge to make decisions. Each ingredient has a different cooking time and we want everything to be served hot by the robot at the same time. We need to provide the function with the data about what we like, the function could 'talk' to the fridge, and the function could control the robot.
A function normally has a name, parentheses and braces. Like this:
Note that
/*...*/
and//
stop code being read by the browser.NAME: You can call a function just about whatever word you want. The example "cookMeal" is typical in joining two words together and giving the second one a capital letter at the beginning - but this is not necessary. It can't have a space in it, and it can't be a number on its own.
PARENTHESES: "Parentheses" or
()
are the letter box on the JavaScript function factory's door or a post box in the street for sending packets of information to the factory. Sometimes the postbox might be marked for examplecookMeal(you, me, yourFriend, myFriend, fridge, dinnerTime)
, in which case you know what data you have to give it.BRACES: "Braces" which look like this
{}
are the tinted windows of our factory. From inside the factory you can see out, but from the outside you can't see in.THE LONG CODE EXAMPLE ABOVE
Our code begins with the word function, so we know that it is one! Then the name of the function sing - that's my own description of what the function is about. Then parentheses (). The parentheses are always there for a function. Sometimes they are empty, and sometimes they have something in. This one has a word in:
(person)
. After this there is a brace like this{
. This marks the start of the function sing(). It has a partner which marks the end of sing() like this}
So this function might have something to do with singing, and might need some data about a person. It has instructions inside to do something with that data.
Now, after the function sing(), near the end of the code is the line
VARIABLE: The letters var stand for "variable". A variable is like an envelope. On the outside this envelope is marked "person". On the inside it contains a slip of paper with the information our function needs, some letters and spaces joined together like a piece of string (it's called a string) that make a phrase reading "an old lady". Our envelope could contain other kinds of things like numbers (called integers), instructions (called functions), lists (called arrays). Because this variable is written outside of all the braces
{}
, and because you can see out through the tinted windows when you are inside the braces, this variable can be seen from anywhere in the code. We call this a 'global variable'.GLOBAL VARIABLE: person is a global variable, meaning that if you change its value from "an old lady" to "a young man", the person will keep being a young man until you decide to change it again and that any other function in the code can see that it's a young man. Press the F12 button or look at the Options settings to open the developer console of a browser and type "person" to see what this value is. Type
person="a young man"
to change it and then type "person" again to see that it has changed.After this we have the line
This line is calling the function, as if it were calling a dog
When the browser has loaded the JavaScript code an reached this line, it will start the function. I put the line at the end to make sure that the browser has all the information it needs to run it.
Functions define actions - the main function is about singing. It contains a variable called firstPart which applies to the singing about the person that applies to each of the verses of the song: "There was " + person + " who swallowed". If you type firstPart into the console, you won't get an answer because the variable is locked up in a function - the browser can't see inside the tinted windows of the braces.
CLOSURES: The closures are the smaller functions that are inside the big sing() function. The little factories inside the big factory. They each have their own braces which mean that the variables inside them can't be seen from the outside. That's why the names of the variables (creature and result) can be repeated in the closures but with different values. If you type these variable names in the console window, you won't get its value because it's hidden by two layers of tinted windows.
The closures all know what the sing() function's variable called firstPart is, because they can see out from their tinted windows.
After the closures come the lines
The sing() function will call each of these functions in the order they are given. Then the sing() function's work will be done.