javascript - 显示单击了哪个列表项

标签 javascript events closures listener

我一直在尝试了解 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 已被单击”。我相信它会发生,因为程序的流程是这样的:

  1. 循环遍历所有列表项并向它们添加事件监听器。 changeHTML 尚未被调用,因为它是对该函数的引用(末尾没有 ())。

  2. i = 5时,循环结束,程序正在等待点击。

  3. 当发生点击时,无论点击哪个列表项,都会调用 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,而无需单击列表项。我对此很困惑。我怀疑它会自行打印,因为我在函数末尾添加了 () ,因此它被调用。问题:

  1. 这里的执行顺序是什么?

  2. 为什么在循环结束后调用changeHTML()?为什么每次迭代后不调用它?

  3. 为什么不等待点击?

第三个示例 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();

所以,我不明白为什么它能以这种方式正常工作。

  1. 现在在循环内部我正在调用 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/

    相关文章:

    javascript - JQuery 和 CSS 之间的实时编辑桥梁

    python - 如何更新事件驱动的情节?

    C 中的闭包 - 这行得通吗?

    javascript - 这是立即调用的函数表达式吗?

    closures - Smalltalk是否有闭包?

    javascript - 如何配置 `direflow-webpack.js`以支持 `sass`文件

    javascript - 将页脚贴在底部 - 没有 jQuery

    javascript - 如何从 json 重建表?

    javascript - 预加载音频文件/事件?

    c# - 窗口加载和 WPF