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.
关于闭包的一个简单解释:
- 取一个函数.我们叫它F.
- 列出F的所有变量.
- 变量可以有两种类型:
- 局部变量(绑定变量)
- 非局部变量(自由变量)
- 如果F没有自由变量,那么它就不能是闭包.
- If F has any free variables (which are defined in a parent scope of F) then:
- F必须只有一个父作用域,它绑定了a个自由变量.
- 如果F在that父范围之外是referenced,则它成为that自由变量的闭包.
- That个自由变量称为闭包F的一个上限值.
现在,让我们使用它来确定谁使用闭包,谁不使用(为了解释起见,我已经对函数进行了命名):
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);
})();
}
在上面的程序中有两个函数:f
和g
.让我们看看它们是否是闭包:
对于f
:
- List the variables:
i2
是一个local变量.
i
是一个free变量.
setTimeout
是一个free变量.
g
是一个local变量.
console
是一个free变量.
- Find the parent scope to which each free variable is bound:
i
是全局的bound.
setTimeout
对于全局范围是bound.
console
对于全局范围是bound.
- 函数referenced在哪个范围内?global scope号.
- 以是
i
不是closed over乘以f
.
- 因此
setTimeout
不是closed over乘以f
.
- 因此
console
不是closed over乘以f
.
因此,函数f
不是闭包.
g
美元:
- List the variables:
console
是一个free变量.
i2
是一个free变量.
- Find the parent scope to which each free variable is bound:
console
对于全局范围是bound.
i2
等于bound等于f
.
- 功能referenced在哪个范围内?scope of 100.
因此,函数g
是自由变量i2
(它是g
的上值)when的闭包,它是setTimeout
内的referenced.
Bad for you:您的朋友正在使用闭合.内部函数是一个闭包.
Case 2: Your Program
for (var i = 0; i < 10; i++) {
setTimeout((function f(i2) {
return function g() {
console.log(i2);
};
})(i), 1000);
}
在上面的程序中有两个函数:f
和g
.让我们看看它们是否是闭包:
对于f
:
- List the variables:
i2
是一个local变量.
g
是一个local变量.
console
是一个free变量.
- Find the parent scope to which each free variable is bound:
console
对于全局范围是bound.
- In which scope is the function referenced? The global scope.
- 因此,
console
不是closed over乘以f
.
因此,函数f
不是闭包.
g
美元:
- List the variables:
console
是一个free变量.
i2
是一个free变量.
- Find the parent scope to which each free variable is bound:
console
对于全局范围是bound.
i2
等于bound等于f
.
- 功能referenced在哪个范围内?scope of 100.
因此,函数g
是自由变量i2
(它是g
的上值)when的闭包,它是setTimeout
内的referenced.
Good for you:你正在使用闭包.内部功能是一个闭包.
所以你和你的朋友都在使用闭包.别再争论了.我希望我澄清了闭包的概念,以及如何为你们两人识别闭包.
Edit:关于为什么所有函数都是闭包的简单解释(credits@Peter):
首先让我们考虑下面的程序(这是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"));
}
}
- 我们知道
lexicalScope
和regularFunction
都不是from the above definition.
- 当我们执行程序we expect时,
message
将被警告because regularFunction
不是一个闭包(即,它可以访问all其父范围内的变量,包括message
).
- 当我们执行程序we observe时,
message
确实被警告了.
接下来让我们考虑下面的程序(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"));
};
}
- 我们知道只有
closureFunction
是一个from the above definition.
- 当我们执行程序we expect
message
not be alerted because closureFunction
是一个闭包(即,它只能访问the time the function is created(see this answer)处的所有non-local variables,这不包括message
).
- 当我们执行程序we observe时,
message
实际上被提醒了.
从这一点我们可以推断出什么呢?
- JavaScript解释器处理闭包的方式与处理其他函数的方式没有什么不同.
- Every function carries its scope chain along with it. Closures don't have a separate referencing environment.
- 闭包和其他函数一样.当它们在范围outside中为referenced时,我们称之为闭包.它们所属的范围because这是一个有趣的例子.