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 by f
.
- Hence
setTimeout
is not closed over by f
.
- Hence
console
is not closed over by f
.
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 of f
.
- In which scope is the function referenced? The scope of
setTimeout
.
- Hence
console
is not closed over by g
.
- Hence
i2
is closed over by g
.
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 by f
.
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 of f
.
- In which scope is the function referenced? The scope of
setTimeout
.
- Hence
console
is not closed over by g
.
- Hence
i2
is closed over by g
.
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"));
}
}
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…