不要在循环中创建函数 Javascript

Don't make functions within loops Javascript

我知道可能有三百个这样的问题,我明白为什么不这样做。如果我们循环说一个常规的 for 循环,每次迭代我们都会创建一个匿名函数表达式,它会使用更多内存。相反,我们将函数置于循环之外,从而给它起一个名字

匿名函数迭代示例

var elements = document.getElementsByClassName('elementName');
for(var i=0; i < elements.length; i++ )
{
  elements[i].addEventListener('click',function(e){
    console.log(e);
  });
}

命名函数迭代示例

function handleClickEvents(e) {
   console.log(e);
}

var elements = document.getElementsByClassName('elementName');
for(var i=0; i < elements.length; i++ )
{
  elements[i].addEventListener('click',handleClickEvents);
}

这里的问题是试图向某人证明此逻辑,老实说,我的 jsperfs 完全反驳了我。请自行查看测试结果here

那么 jsPerf 只是计算错误还是这只是一个完全破灭的神话?我看到通过 运行 匿名函数作为我的 eventListener 函数,与阶梯相比我获得了速度。

任何人都可以告诉我这里的交易是什么以及为什么如果我们在第一个示例中获得更快的速度,我还要从第二个版本中再多两行吗?

我认为您的比较有误。如果你要反转代码。将匿名函数放在比较的后面。它会更慢。 (http://jsperf.com/best-event-listener-practice/5)。后面的代码总是会比较慢,因为之前已经做了很多绑定。

您不必担心性能问题——我很难想象您要添加数百万个事件侦听器。

第二种选择(指定函数引用)的优越性在于函数一旦定义,就可以在其他地方使用。它需要更少的 });,因此不容易出现拼写错误。也许更重要的是,它可能更具可读性。让我们以将函数传递给 Array#filter 为例,以检查文件名是否为 jpg:

names.filter(function(name) { 
    return /\.jpg$/i.test(name);
});

对比

function isJpeg(name) { return /\.jpg$/i.test(name); }

names.filter(isJpeg);

如果将方法链接在一起,好处会更加明显:

names . filter(isJpeg) . map(makeThumbnail) . forEach(uploadJpg);

归根结底,这并不重要,归结为个人喜好,但有一点很清楚,性能问题不应该成为驱动您做出决定的因素,除非在非常特殊的情况下。一个好的通用规则是编写非常短的、一次性的内联函数。使用 ES6 和箭头函数,更多的函数可以 "very short" 成为内联的候选对象。

顺便说一句,即使是内联编写函数,给它起个名字通常也是个好主意:

names.filter(function isJpeg(name) { 

这有两个好处。首先,它是 documentation/comment 的一种形式,可以帮助人们阅读您的代码。其次,大多数调试器和堆栈跟踪会更好地报告函数。大多数缩小器会删除名称,因此不会影响生产。