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:
i2is a local variable.iis a free variable.setTimeoutis a free variable.gis a local variable.consoleis a free variable.
- Find the parent scope to which each free variable is bound:
iis bound to the global scope.setTimeoutis bound to the global scope.consoleis bound to the global scope.
- In which scope is the function referenced? The global scope.
- Hence
iis not closed over byf. - Hence
setTimeoutis not closed over byf. - Hence
consoleis not closed over byf.
- Hence
Thus the function f is not a closure.
For g:
- List the variables:
consoleis a free variable.i2is a free variable.
- Find the parent scope to which each free variable is bound:
consoleis bound to the global scope.i2is bound to the scope off.
- In which scope is the function referenced? The scope of
setTimeout.- Hence
consoleis not closed over byg. - Hence
i2is 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:
i2is a local variable.gis a local variable.consoleis a free variable.
- Find the parent scope to which each free variable is bound:
consoleis bound to the global scope.
- In which scope is the function referenced? The global scope.
- Hence
consoleis not closed over byf.
- Hence
Thus the function f is not a closure.
For g:
- List the variables:
consoleis a free variable.i2is a free variable.
- Find the parent scope to which each free variable is bound:
consoleis bound to the global scope.i2is bound to the scope off.
- In which scope is the function referenced? The scope of
setTimeout.- Hence
consoleis not closed over byg. - Hence
i2is 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
lexicalScopeandregularFunctionaren’t closures from the above definition. - When we execute the program we expect
messageto be alerted becauseregularFunctionis 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
messageis 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
closureFunctionis a closure from the above definition. - When we execute the program we expect
messagenot to be alerted becauseclosureFunctionis 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
messageis 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.