其中大部分将按照您期望的方式工作,除了这部分:
button.addEventListener("click", function (e) {
alert(i);
}, false);
人们可能期望每个都addEventListener
获得三个参数:
"click"
- 函数的结果
false
如果是这种情况,五个事件侦听器将如下所示:
button.addEventListener("click", alert(0), false);
button.addEventListener("click", alert(1), false);
button.addEventListener("click", alert(2), false);
button.addEventListener("click", alert(3), false);
button.addEventListener("click", alert(4), false);
但是,设置为第二个参数的不是函数的结果,而是函数本身。所以实际上,这里有五个事件监听器:
button.addEventListener("click", function(e){alert(i);}, false);
button.addEventListener("click", function(e){alert(i);}, false);
button.addEventListener("click", function(e){alert(i);}, false);
button.addEventListener("click", function(e){alert(i);}, false);
button.addEventListener("click", function(e){alert(i);}, false);
如您所见,它们的第二个参数是以下函数:
function(e){
alert(i);
}
因此,当您单击其中一个按钮时,将执行上述功能。因此,您单击一个按钮并执行该函数,这是一个可操作的问题: 的值是i
多少?上下文在这里很重要。上下文不是循环的中间,上下文是你点击了按钮并触发了这个函数。
一个聪明的猜测i
是在这种情况下是未定义的。但是当一个函数在另一个函数中创建时会发生这个棘手的事情:子函数可以访问父函数中的变量。所以i
还是可以的!那么现在是什么i
?它是5
,因为我们已经将它循环到 5,它仍然是 5。
所以当你听到人们谈论闭包时,他们真正的意思是“子功能”。子函数很棘手,因为它们以这种方式携带其父母的变量。
那么如果你想让按钮alert
的编号对应于被点击的按钮呢?如果您使用一个立即执行的函数,则可以这样做,因此返回函数的结果,而不是函数本身。为此,您只需将函数表达式包装在括号中,然后使用();
.
var one = function(){} // one contains a function.
var two = (function(){})(); // two contains the result of a function
让我们稍微玩一下,看看它会如何影响代码。
我们可以让事件监听器的第二个参数立即执行,如下所示:
button.addEventListener("click", (function (e) {
alert(i);
})(), false);
然后你会在加载页面时立即收到五个警报,0-4,单击时什么也没有,因为该函数没有返回任何内容。
这不是我们真正想要的。我们真正想要的是addEventListener
从那个循环中触发。Easy peasy——只需将其包装在一个立即执行的函数中:
(function(i){
button.addEventListener("click", function (e) {
alert(i);
}, false);
})(i);
请注意,不是();
我们(i);
现在有,因为我们将参数传递i
给它,但除此之外它都是一样的。现在该函数立即被触发,也就是我们将立即获得该函数的结果。这个新的立即执行功能的结果是什么?它实际上会根据它所在的循环迭代而变化。以下是循环每次迭代的函数结果:
button.addEventListener("click", function (e) {
alert(0);
}, false);
button.addEventListener("click", function (e) {
alert(1);
}, false);
button.addEventListener("click", function (e) {
alert(2);
}, false);
button.addEventListener("click", function (e) {
alert(3);
}, false);
button.addEventListener("click", function (e) {
alert(4);
}, false);
因此,您现在可以看到,在单击时触发的五个函数都具有i
硬编码的值。如果你得到这个,你就会得到关闭。就个人而言,我不明白为什么人们在解释问题时会过度复杂化。请记住以下几点:
- 子函数可以访问其父函数的变量。
- 用作表达式的函数(例如,分配给变量或其他东西,未定义为
function newFunc(){}
)用作函数而不是函数的结果。
- 如果你想要函数的结果,你可以通过包装它立即执行函数
( ... )();
就我个人而言,我发现这是一种更直观的方式来理解闭包,而无需使用所有疯狂的术语。