我一直试图围绕Javascript中的闭包以及如何执行(或者更确切地说 - 执行它们的顺序)。
这是我的例子:假设我有一个无序的项目列表,当我点击其中一个时,我想打印出哪个项目被点击了。我知道解决方案,但我不明白为什么它的工作方式。所以下面我将发布我的代码的一些变体,并给出我的相信它如何工作的想法。我希望有人对此发表评论。如果可能,请使用尽可能少的行话。想象一下,你正和一个5岁的孩子说话。
第一个例子 https://jsfiddle.net/wLpcpa5q/
我的HTML:
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
<li>Item 5</li>
</ul>
<span id="span"></span>
JS:
var items = document.getElementsByTagName('li');
var span = document.getElementById('span');
function clickHandler() {
function changeHTML() {
span.innerHTML = ('item ' + i + ' was clicked');
}
for (var i = 0, len = items.length; i < len; i++) {
items[i].addEventListener("click", changeHTML);
}
}
clickHandler();
如果我运行它,无论我点击哪个项目,我都会得到'项目5被点击'。我相信它的发生是因为程序的流程如下:
循环遍历所有列表项并向其添加事件侦听器。 changeHTML
尚未调用,因为它是对该函数的引用(最后没有()
)。
当i = 5
循环结束且程序正在等待点击时。
当点击发生changeHTML
时,无论点击哪个列表项,都会调用i = 5
。
这是对的吗?
继续前进。
第二个例子 https://jsfiddle.net/wLpcpa5q/1/
而不是changeHTML
我在循环中使用changeHTML()
for (var i = 0, len = items.length; i < len; i++) {
items[i].addEventListener("click", changeHTML());
}
这会打印出item 4 was clicked
,而不会点击列表项。我很困惑。我怀疑它打印自己,因为我在函数的末尾添加了(),因此它被调用。问题:
这里的执行顺序是什么?
为什么在循环结束后调用changeHTML()
?为什么不在每次迭代后调用它?
为什么不等待点击?
第三个例子 https://jsfiddle.net/wLpcpa5q/2/
这是一个使用闭包的工作解决方案,这个概念我真的不明白:
function clickHandler() {
function wrap(i) {
return function changeHTML() {
span.innerHTML = 'item ' + (i + 1) + ' was clicked';
}
}
for (var i = 0, len = items.length; i < len; i++) {
items[i].addEventListener("click", wrap(i));
}
}
clickHandler();
所以,我不明白为什么它以这种方式正常工作。
wrap(i)
,因为(),它看起来像第二个例子。但与第二个例子不同的是,该功能正在等待点击。这是为什么? 2.我猜一切正常,因为现在我正在返回内部函数changeHTML
而不是仅仅将它放在clickHandler
中。但我不明白返回changeHTML
意味着什么。
谢谢。
答案 0 :(得分:0)
首先在点击之前完成循环完成,因此i = 5 只有在单击
时才会调用changehtml第二个例子是在每个循环中调用changehtml而不是点击,你添加一个空的监听器,因为添加的监听器是changehtml将返回的,你不会返回任何函数引用。
那么为什么第三个工作呢? 因为这次你真的添加了一个函数作为监听器,是的,你返回一个函数。 点击后会发生什么:
在第一种情况下它无法工作,因为我在clickHandler中是相同的,最后一个值是5.
在最后一种情况下,您创建了5个不同的函数,这些函数都有自己的i