编者注:JavaScript 中的所有函数都是闭包,如此处所述post http://javascriptweblog.wordpress.com/2010/10/25/understanding-javascript-closures/。然而,我们只对识别这些函数的子集感兴趣,它们是有趣的 https://developer.mozilla.org/en-US/docs/JavaScript/Guide/Closures从理论的角度来看。从此以后任何对这个词的引用closure除非另有说明,否则将引用该函数子集。
对闭包的简单解释:
- 取一个函数。我们姑且称之为F吧。
- 列出F的所有变量。
- The variables may be of two types:
- 局部变量(绑定变量)
- 非局部变量(自由变量)
- 如果 F 没有自由变量,那么它不能是闭包。
- If F has any free variables (which are defined in a parent scope of F) then:
- F 必须只有一个父作用域a自由变量是绑定的。
- If F is 引用的从外面that父作用域,那么它就变成了闭包that自由变量。
-
That自由变量称为闭包 F 的上值。
现在让我们用它来找出谁使用闭包,谁不使用(为了解释起见,我命名了这些函数):
案例 1:您朋友的程序
for (var i = 0; i < 10; i++) {
(function f() {
var i2 = i;
setTimeout(function g() {
console.log(i2);
}, 1000);
})();
}
上面的程序中有两个函数:f
and g
。让我们看看它们是否是闭包:
For f
:
- List the variables:
-
i2
is a local多变的。
-
i
is a free多变的。
-
setTimeout
is a free多变的。
-
g
is a local多变的。
-
console
is a free多变的。
- Find the parent scope to which each free variable is bound:
-
i
is bound到全球范围。
-
setTimeout
is bound到全球范围。
-
console
is bound到全球范围。
- In which scope is the function referenced? The global scope.
- Hence
i
is not 关闭 by f
.
- Hence
setTimeout
is not 关闭 by f
.
- Hence
console
is not 关闭 by f
.
因此函数f
不是一个闭包。
For g
:
- List the variables:
-
console
is a free多变的。
-
i2
is a free多变的。
- Find the parent scope to which each free variable is bound:
-
console
is bound到全球范围。
-
i2
is bound到范围f
.
- In which scope is the function referenced? The scope of
setTimeout
.
- Hence
console
is not 关闭 by g
.
- Hence
i2
is 关闭 by g
.
因此函数g
是自由变量的闭包i2
(这是一个上值g
) when it's 引用的从内部setTimeout
.
对你不好:你的朋友正在使用闭包。内部函数是一个闭包。
案例 2:您的程序
for (var i = 0; i < 10; i++) {
setTimeout((function f(i2) {
return function g() {
console.log(i2);
};
})(i), 1000);
}
上面的程序中有两个函数:f
and g
。让我们看看它们是否是闭包:
For f
:
- List the variables:
-
i2
is a local多变的。
-
g
is a local多变的。
-
console
is a free多变的。
- Find the parent scope to which each free variable is bound:
-
console
is bound到全球范围。
- In which scope is the function referenced? The global scope.
- Hence
console
is not 关闭 by f
.
因此函数f
不是一个闭包。
For g
:
- List the variables:
-
console
is a free多变的。
-
i2
is a free多变的。
- Find the parent scope to which each free variable is bound:
-
console
is bound到全球范围。
-
i2
is bound到范围f
.
- In which scope is the function referenced? The scope of
setTimeout
.
- Hence
console
is not 关闭 by g
.
- Hence
i2
is 关闭 by g
.
因此函数g
是自由变量的闭包i2
(这是一个上值g
) when it's 引用的从内部setTimeout
.
对你有好处:您正在使用闭包。内部函数是一个闭包。
所以你和你的朋友都在使用闭包。别再争论了。我希望我已经清楚了闭包的概念以及如何为你们俩识别它们。
Edit:关于为什么所有函数都是闭包的简单解释(鸣谢@Peter):
首先让我们考虑以下程序(它是control http://jsfiddle.net/KyQKw/):
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"));
}
}
- 我们知道,两者
lexicalScope
and regularFunction
不是关闭从上面的定义.
- 当我们执行程序时我们期待
message
被提醒because regularFunction
不是一个闭包(即它可以访问all其父作用域中的变量 - 包括message
).
- 当我们执行程序时我们观察到 that
message
确实是警觉了。
接下来让我们考虑以下程序(它是选择 http://jsfiddle.net/KyQKw/1/):
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"));
};
}
- 我们知道,只有
closureFunction
是一个闭包从上面的定义.
- 当我们执行程序时我们期待
message
不被警告because closureFunction
是一个闭包(即它只能访问它的所有非局部变量 at 函数创建的时间 (看到这个答案 https://stackoverflow.com/a/12930799/783743) - 这不包括message
).
- 当我们执行程序时我们观察到 that
message
实际上正在被警告。
我们由此推断出什么?
- JavaScript 解释器对待闭包的方式与对待其他函数的方式没有什么不同。
- 每个函数都有它的作用域链 http://dmitrysoshnikov.com/ecmascript/chapter-4-scope-chain/与它一起。闭包没有separate参考环境。
- 闭包就像其他所有函数一样。当它们发生时我们就称它们为闭包引用的在一定范围内outside他们所属的范围because这是一个有趣的案例。