A friend of mine and I are currently discussing what is a closure in JS and what isn't. We just want to make sure we really understand it correctly.
Let's take this example. We have a counting loop and want to print the counter variable on the console delayed. Therefore we use setTimeout
and closures to capture the value of the counter variable to make sure that it will not print N times the value N.
The wrong solution without closures or anything near to closures would be:
for(var i = 0; i < 10; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
which will of course print 10 times the value of i
after the loop, namely 10.
So his attempt was:
for(var i = 0; i < 10; i++) {
(function(){
var i2 = i;
setTimeout(function(){
console.log(i2);
}, 1000)
})();
}
printing 0 to 9 as expected.
I told him that he isn't using a closure to capture i
, but he insists that he is. I proved that he doesn't use closures by putting the for loop body within another setTimeout
(passing his anonymous function to setTimeout
), printing 10 times 10 again. The same applies if I store his function in a var
and execute it after the loop, also printing 10 times 10. So my argument is that he doesn't really capture the value of i
, making his version not a closure.
My attempt was:
for(var i = 0; i < 10; i++) {
setTimeout((function(i2){
return function() {
console.log(i2);
}
})(i), 1000);
}
So I capture i
(named i2
within the closure), but now I return another function and pass this around. In my case, the function passed to setTimeout really captures i
.
Now who is using closures and who isn't?
Note that both solutions print 0 to 9 on the console delayed, so they solve the original problem, but we want to understand which of those two solutions uses closures to accomplish this.
After inspecting closely, looks like both of you are using closure.
In your friends case,
i
is accessed inside anonymous function 1 andi2
is accessed in anonymous function 2 where theconsole.log
is present.In your case you are accessing
i2
inside anonymous function whereconsole.log
is present. Add adebugger;
statement beforeconsole.log
and in chrome developer tools under "Scope variables" it will tell under what scope the variable is.I would like to share my example and an explanation about closures. I made a python example, and two figures to demonstrate stack states.
The output of this code would be as follows:
Here are two figures to show stacks and the closure attached to the function object.
when the function is returned from maker
when the function is called later
When the function is called through a parameter or a nonlocal variable, the code needs local variable bindings such as margin_top, padding as well as a, b, n. In order to ensure the function code to work, the stack frame of the maker function which was gone away long ago should be accessible, which is backed up in the closure we can find along with the function message object.
I wrote this a while ago to remind myself of what a closure is and how it works in JS.
A closure is a function that, when called, uses the scope in which it was declared, not the scope in which it was called. In javaScript, all functions behave like this. Variable values in a scope persist as long as there is a function that still points to them. The exception to the rule is 'this', which refers to the object that the function is inside when it is called.
In a nutshell Javascript Closures allow a function to access a variable that is declared in a lexical-parent function.
Let's see a more detailed explanation. To understand closures it is important to understand how JavaScript scopes variables.
Scopes
In JavaScript scopes are defined with functions. Every function defines a new scope.
Consider the following example;
calling f prints
Let's now consider the case we have a function
g
defined within another functionf
.We will call
f
the lexical parent ofg
. As explained before we now have 2 scopes; the scopef
and the scopeg
.But one scope is "within" the other scope, so is the scope of the child function part of the scope of the parent function? What happens with the variables declared in the scope of the parent function; will I be able to access them from the scope of the child function? That's exactly where closures step in.
Closures
In JavaScript the function
g
can not only access any variables declared in scopeg
but also access any variables declared in the scope of the parent functionf
.Consider following;
calling f prints
Let's look at the line
console.log(foo);
. At this point we are in scopeg
and we try to access the variablefoo
that is declared in scopef
. But as stated before we can access any variable declared in a lexical parent function which is the case here;g
is the lexical parent off
. Thereforehello
is printed.Let's now look at the line
console.log(bar);
. At this point we are in scopef
and we try to access the variablebar
that is declared in scopeg
.bar
is not declared in the current scope and the functiong
is not the parent off
, thereforebar
is undefinedActually we can also access the variables declared in the scope of a lexical "grand parent" function. Therefore if there would be a function
h
defined within the functiong
then
h
would be able to access all the variables declared in the scope of functionh
,g
, andf
. This is done with closures. In JavaScript closures allows us to access any variable declared in the lexical parent function, in the lexical grand parent function, in the lexical grand-grand parent function, etc. This can be seen as a scope chain;scope of current function -> scope of lexical parent function -> scope of lexical grand parent function -> ...
until the last parent function that has no lexical parent.The window object
Actually the chain doesn't stop at the last parent function. There is one more special scope; the global scope. Every variable not declared in a function is considered to be declared in the global scope. The global scope has two specialities;
window
object.Therefore there are exactly two ways of declaring a variable
foo
in the global scope; either by not declaring it in a function or by setting the propertyfoo
of the window object.Both attempts uses closures
Now that you have read a more detailed explanation it may now be apparent that both solutions uses closures. But to be sure, let's make a proof.
Let's create a new Programming Language; JavaScript-No-Closure. As the name suggests, JavaScript-No-Closure is identical to JavaScript except it doesn't support Closures.
In other words;
Alright, let's see what happens with the first solution with JavaScript-No-Closure;
therefore this will print
undefined
10 times in JavaScript-No-Closure.Hence the first solution uses closure.
Let's look at the second solution;
therefore this will print
undefined
10 times in JavaScript-No-Closure.Both solutions uses closures.
Edit: It is assumed that these 3 code snippets are not defined in the global scope. Otherwise the variables
foo
andi
would be bind to thewindow
object and therefore accessible through thewindow
object in both JavaScript and JavaScript-No-Closure.Closure
A closure is not an function, and not an expression. It must be seen as a kind of 'snapshot' from the used variables outside the functionscope and used inside the function. Grammatically, one should say: 'take the closure of the variables'.
Again, in other words: A closure is a copy of the relevant context of variables on which the function depends on.
Once more (naïf): A closure is having access to variables who are not being passed as parameter.
Bear in mind that these functional concepts strongly depends upon the programming language / environment you use. In JavaScript, the closure depends on lexical scoping (which is true in most c-languages).
So, returning a function is mostly returning a anonymous / unnamed function. When the function access variables, not passed as parameter, and within its (lexical) scope, a closure has been taken.
So, concerning your examples:
All are using closures. Don't confuse the point of execution with closures. If the 'snapshot' of the closures is taken at the wrong moment, the values may be unexpected but certainly a closure is taken!
According to the
closure
definition:You are using
closure
if you define a function which use a variable which is defined outside of the function. (we call the variable a free variable).They all use
closure
(even in the 1st example).