问题描述
所有函数都是闭包,如本文所述。然而,我们只对确定这些函数的子集感兴趣,这些函数从理论的角度来看是有趣的。 此后,除非另有说明,否则对闭包 一词的任何引用都将指代此功能子集。
闭包的简单解释:
- 取一个函数。我们称它为 F。
- 列出 F 的所有变量。
- 变量可能有两种类型:
- 局部变量(绑定变量)
- 非局部变量(自由变量)
- 如果 F 没有自由变量,则它不能是闭包。
- 如果 F 有任何自由变量(在F 父范围中定义),则:
- F 的父作用域必须只有一个 变量绑定到的父作用域。
- 如果 F从 ***
- 自由变量称为闭包 F 的上值。
现在让我们用它来确定谁使用闭包,谁不使用(为了解释起见,我命名了函数):
for (var i = 0; i < 10; i++) {
(function f() {
var i2 = i;
setTimeout(function g() {
console.log(i2);
}, 1000);
})();
}
在上面的程序中有两个函数:f
和g
。让我们看看它们是否是闭包:
对于f
:
- 列出变量:
- 查找每个自由变量绑定到的父作用域:
-
i
到全局范围。 -
setTimeout
到全局范围。 -
console
到全局范围。
-
-
在哪个范围内? 范围 。
- 因此
i
不被f
。 - 因此
setTimeout
不被f
。 - 因此
console
不被f
。
- 因此
因此该函数f
不是闭包。
对于g
:
- 列出变量:
- 查找每个自由变量绑定到的父作用域:
-
console
到全局范围。 -
i2
在的范围内f
。
-
-
在哪个范围内?的 。
- 因此
console
不被g
。 - 因此
i2
被g
。_
- 因此
因此, 当* 从内部 g
该函数时,该函数是自由变量i2
(它是 的上值)g
的闭包。 ***setTimeout
for (var i = 0; i < 10; i++) {
setTimeout((function f(i2) {
return function g() {
console.log(i2);
};
})(i), 1000);
}
在上面的程序中有两个函数:f
和g
。让我们看看它们是否是闭包:
对于f
:
- 列出变量:
- 查找每个自由变量绑定到的父作用域:
-
console
到全局范围。
-
-
在哪个范围内? 范围 。
- 因此
console
不被f
。
- 因此
因此该函数f
不是闭包。
对于g
:
- 列出变量:
- 查找每个自由变量绑定到的父作用域:
-
console
到全局范围。 -
i2
在的范围内f
。
-
-
在哪个范围内?的 。
- 因此
console
不被g
。 - 因此
i2
被g
。_
- 因此
因此, 当* 从内部 g
该函数时,该函数是自由变量i2
(它是 的上值)g
的闭包。 ***setTimeout
所以你和你的朋友都在使用闭包。别吵了。我希望我清除了闭包的概念以及如何为你们俩识别它们。
首先让我们考虑以下程序(它是控件):
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"));
}
}
- *
- 当我们执行程序时,
message
收到警报regularFunction
它不是闭包(即它可以访问其父范围内的message
变量 - 包括)。 - 当我们执行程序时, 到
message
它确实被警告了。
接下来让我们考虑以下程序(它是替代方案):
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"));
};
}
- *
- 当我们执行程序时,
message
不会收到警报 它是一个闭包(即它只能在closureFunction
访问其所有 。”范围 - JavaScript 闭包与匿名函数 - 代码日志”)message
- 当我们执行程序时, 到
message
它实际上是被警告的。
我们从中推断出什么?
- JavaScript 解释器对待闭包的方式与对待其他函数的方式没有区别。
- 每个函数都带有它的作用域链。闭包没有 的引用环境。
- 闭包就像所有其他函数一样。当它们在它们所属范围 之外的范围内被* 时,我们只称它们为闭包, 这是一个有趣的案例。 ***
解决方法
我和我的一个朋友目前正在讨论什么是 JS 中的闭包,什么不是。我们只是想确保我们真正正确地理解它。
让我们举这个例子。我们有一个计数循环,并希望在控制台上延迟打印计数器变量。因此,我们使用setTimeout
和 闭包
来捕获计数器变量的值,以确保它不会打印 N 次值 N。
没有闭包 或任何接近 闭 包的错误解决方案是:
for(var i = 0; i < 10; i++) {
setTimeout(function() {
console.log(i);
},1000);
}
这当然会打印i
循环后值的 10 倍,即 10。
所以他的尝试是:
for(var i = 0; i < 10; i++) {
(function(){
var i2 = i;
setTimeout(function(){
console.log(i2);
},1000)
})();
}
按预期打印 0 到 9。
我告诉他他没有使用 闭包 来捕获i
,但他坚持认为他是。我通过将 for 循环体放入另一个循环体(将他的匿名函数传递给)来证明他不使用
闭包 ,再次打印 10 次 10 次。如果我将他的函数存储在 a 中并在循环 之后 执行它,同样适用,也打印 10 乘以 10。所以我的论点是
他并没有真正 捕获 的值 ,使他的版本 不是 闭包。setTimeout``setTimeout``var
__i
我的尝试是:
for(var i = 0; i < 10; i++) {
setTimeout((function(i2){
return function() {
console.log(i2);
}
})(i),1000);
}
所以我捕获(在闭包中i
命名),但现在我 返回 另一个函数并传递它。 就我而言,传递给 setTimeout 的函数确实捕获了
.i2
__ i
现在谁在使用闭包,谁不使用?
请注意,这两种解决方案都延迟在控制台上打印 0 到 9,因此它们解决了原始问题,但我们想了解这两种解决方案中的哪一个 使用闭包 来完成此任务。