我一直在尝试了解 Javascript 中的闭包以及事物是如何执行的(或者更确切地说 - 它们执行的顺序)。
这是我的示例:假设我有一个无序列表的项目,当我单击其中一个项目时,我想打印出单击的项目。我知道解决方案,但我不明白为什么它会这样工作。因此,下面我将发布我的代码的一些变体,并给出我对它如何工作的想法。我希望有人对此发表评论。如果可能的话,尽可能少使用行话。想象一下您正在和一个 5 岁的 child 说话。
第一个示例 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
是什么意思。
- 所以,我再次需要有人解释一下这里执行的顺序、在哪个时间点调用哪些函数、它与循环的迭代有何关系等。
谢谢。
最佳答案
首先举个例子,循环在点击之前完成,所以 i = 5 并且仅当您单击时才会调用changehtml
第二个示例changehtml在每个循环而不是在单击时调用,您添加一个空监听器,因为添加的监听器是changehtml将返回的内容,并且您不返回任何函数引用。
那么为什么第三个有效呢? 因为这次你真的添加了一个函数作为监听器,是的,你返回了一个函数。 点击后会发生什么:
- changeHTML 被调用,它会查找最接近的 i 声明,这里 i 是一个参数,因此他采用作为参数传递的“i”。
在第一种情况下它无法工作,因为 i 在 clickHandler 中是相同的,并且最后一个值为 5。
在最后一个例子中,您创建了 5 个不同的函数,它们有自己的 i
关于javascript - 显示单击了哪个列表项,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48252456/