Editor’s Note: All functions in JavaScript are closures as explained in this post. However we are only interested in identifying a subset of these functions which are interesting from a theoretical point of view. Henceforth any reference to the word closure will refer to this subset of functions unless otherwise stated.
A simple explanation for closures:
- Take a function. Let’s call it F.
- List all the variables of F.
- The variables may be of two types:
- Local variables (bound variables)
- Non-local variables (free variables)
- If F has no free variables then it cannot be a closure.
- If F has any free variables (which are defined in a parent scope of F) then:
- There must be only one parent scope of F to which a free variable is bound.
- If F is referenced from outside that parent scope, then it becomes a closure for that free variable.
- That free variable is called an upvalue of the closure F.
Now let’s use this to figure out who uses closures and who doesn’t (for the sake of explanation I have named the functions):
Case 1: Your Friend’s Program
for (var i = 0; i < 10; i++) {
(function f() {
var i2 = i;
setTimeout(function g() {
console.log(i2);
}, 1000);
})();
}
In the above program there are two functions: f
and g
. Let’s see if they are closures:
For f
:
- List the variables:
i2
is a local variable.i
is a free variable.setTimeout
is a free variable.g
is a local variable.console
is a free variable.
- Find the parent scope to which each free variable is bound:
i
is bound to the global scope.setTimeout
is bound to the global scope.console
is bound to the global scope.
- In which scope is the function referenced? The global scope.
- Hence
i
is not closed over byf
. - Hence
setTimeout
is not closed over byf
. - Hence
console
is not closed over byf
.
- Hence
Thus the function f
is not a closure.
For g
:
- List the variables:
console
is a free variable.i2
is a free variable.
- Find the parent scope to which each free variable is bound:
console
is bound to the global scope.i2
is bound to the scope off
.
- In which scope is the function referenced? The scope of
setTimeout
.- Hence
console
is not closed over byg
. - Hence
i2
is closed over byg
.
- Hence
Thus the function g
is a closure for the free variable i2
(which is an upvalue for g
) when it’s referenced from within setTimeout
.
Bad for you: Your friend is using a closure. The inner function is a closure.
Case 2: Your Program
for (var i = 0; i < 10; i++) {
setTimeout((function f(i2) {
return function g() {
console.log(i2);
};
})(i), 1000);
}
In the above program there are two functions: f
and g
. Let’s see if they are closures:
For f
:
- List the variables:
i2
is a local variable.g
is a local variable.console
is a free variable.
- Find the parent scope to which each free variable is bound:
console
is bound to the global scope.
- In which scope is the function referenced? The global scope.
- Hence
console
is not closed over byf
.
- Hence
Thus the function f
is not a closure.
For g
:
- List the variables:
console
is a free variable.i2
is a free variable.
- Find the parent scope to which each free variable is bound:
console
is bound to the global scope.i2
is bound to the scope off
.
- In which scope is the function referenced? The scope of
setTimeout
.- Hence
console
is not closed over byg
. - Hence
i2
is closed over byg
.
- Hence
Thus the function g
is a closure for the free variable i2
(which is an upvalue for g
) when it’s referenced from within setTimeout
.
Good for you: You are using a closure. The inner function is a closure.
So both you and your friend are using closures. Stop arguing. I hope I cleared the concept of closures and how to identify them for the both of you.
Edit: A simple explanation as to why are all functions closures (credits @Peter):
First let’s consider the following program (it’s the control):
lexicalScope();
function lexicalScope() {
var message = "This is the control. You should be able to see this message being alerted.";
regularFunction();
function regularFunction() {
alert(eval("message"));
}
}
- We know that both
lexicalScope
andregularFunction
aren’t closures from the above definition. - When we execute the program we expect
message
to be alerted becauseregularFunction
is not a closure (i.e. it has access to all the variables in its parent scope – includingmessage
). - When we execute the program we observe that
message
is indeed alerted.
Next let’s consider the following program (it’s the alternative):
var closureFunction = lexicalScope();
closureFunction();
function lexicalScope() {
var message = "This is the alternative. If you see this message being alerted then in means that every function in JavaScript is a closure.";
return function closureFunction() {
alert(eval("message"));
};
}
- We know that only
closureFunction
is a closure from the above definition. - When we execute the program we expect
message
not to be alerted becauseclosureFunction
is a closure (i.e. it only has access to all its non-local variables at the time the function is created (see this answer) – this does not includemessage
). - When we execute the program we observe that
message
is actually being alerted.
What do we infer from this?
- JavaScript interpreters do not treat closures differently from the way they treat other functions.
- Every function carries its scope chain along with it. Closures don’t have a separate referencing environment.
- A closure is just like every other function. We just call them closures when they are referenced in a scope outside the scope to which they belong because this is an interesting case.