var a = function() vs function a() for event listener? 在事件监听器中,var a = function() 和 function a() 有什么区别?

6

我正在尝试理解将函数添加到事件监听器时的区别以及它所具有的影响。

var buttons = document.getElementsByTagName('button');
for (i = 0, len = 3; i < len; i++) {
    var log = function(e) {
        console.log(i);
    }
    buttons[0].addEventListener("click", log);
}

for (i = 0, len = 3; i < len; i++) {
    function log(e) {
        console.log(i);
    }
    buttons[1].addEventListener("click", log);
}

http://jsfiddle.net/paptd/

第一个按钮会触发console.log三次,而第二个按钮只会触发一次。
在正常情况下,为什么要使用什么来添加事件监听器中的函数?

对我来说,在你的jsfiddle中,两个按钮都按预期触发了三次。 - Fallexe
1
这里有几点需要注意。
  1. 你没有声明 ilen
  2. 块内的函数声明是不好的做法,使用函数表达式代替,那就是问题所在。参考链接
- elclanrs
@Fallexe,用的是什么浏览器?(这是一个糟糕的浏览器,因为它没有遵循Row W引用的W3C规范。) - gdoron
@basilikum,您介意解释一下您的更改以及它的好处吗? - user1767586
1
@user1767586,我并没有修改你的代码来改进它。实际上,代码运行的方式与之前相同。我只是想向你展示JavaScript如何在内部重新排列变量声明和函数定义。因此,你发布的代码实际上被解释为按照我发布的方式编写的代码。 - basilikum
显示剩余4条评论
1个回答

5

几点注意:

  • 第一个方法在每次迭代中都会创建一个新的log函数,因此每次添加另一个事件监听器时都会添加一个新的函数。
  • 第二个方法创建了一个全局(关于hoisting的阅读)log函数。如果在同一EventTarget上使用相同参数注册多个相同的EventListener,则重复实例将被丢弃。它们不会导致EventListener被调用两次。

规格说明:

在同一EventTarget上反复使用相同的参数类型、侦听器和useCapture参数调用addEventListener(或removeEventListener)没有任何效果。这样做不会使事件侦听器注册多次,并不会导致触发顺序发生变化。

来源 感谢Rob W.。

因此,第二个和第三个迭代都没有做任何事情。

  • 您还存在闭包问题,最后一次迭代将i设置为3,这也是在控制台中显示的结果。

具有闭包的修正版本:

var buttons = document.getElementsByTagName('button');
for (i = 0, len = 3; i < len; i++) {
    var log = (function closure(number) {
        return function () {
            console.log(number);
        }
    })(i);

    buttons[0].addEventListener("click", log);
}

DEMO


在这种情况下,“不需要多次添加相同的函数”不是一个强有力的论点,您应该明确引用规范。相关引用:“对于具有相同参数typelisteneruseCapture的同一EventTarget重复调用addEventListener(或removeEventListener)没有任何效果。这样做不会导致事件侦听器被注册多次,也不会导致触发顺序发生变化。”-http://dev.w3.org/2006/webapi/DOM-Level-3-Events/html/DOM3-Events.html#interface-EventTarget - Rob W
第二种形式(循环块内的函数声明)实际上是无效的,只能工作是因为浏览器添加了非标准行为来处理它。此外,在第二种情况下,如果包含代码是全局的,则log将仅为全局变量,但是无论如何都应避免在块内进行函数声明。 - Tim Down
@TimDown 就像这样:var foo =2; var foo =2; var foo =2; 我不知道规范,但它可以正常运行... - gdoron
是的,它可以在所有浏览器中运行,但正如所提到的,它需要浏览器JS解析器中的非标准扩展,并且我认为不同的浏览器实现之间存在差异。此外,由于其他ECMAScript环境没有义务支持块内函数声明,因此代码的可移植性较差。它可以轻松地避免,所以最好这样做。更多背景信息:http://kangax.github.io/nfe/#function-statements - Tim Down
@TimDown,我并没有鼓励这样做,只是解释了它的工作原理...(:,不用担心,我的代码看起来有点不同。 - gdoron
我知道,我知道。我只是太懒了,不想写自己的答案,试图借用你的 :) - Tim Down

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接