为什么每个都比常规迭代器更受欢迎?

时间:2018-03-22 05:02:12

标签: javascript for-loop ecmascript-6

我正在阅读airbnb javascript guide。有一个特别的陈述,说:

  

不要使用迭代器。更喜欢JavaScript的高阶函数,而不是像for-in或for-of那样的循环。

他们给出上述陈述的原因是:

  

这会强制执行我们的不可变规则。处理返回值的纯函数比副作用更容易推理。

我无法区分给出的两种编码方法:

    const numbers = [1, 2, 3, 4, 5];

    // bad
    let sum = 0;
    for (let num of numbers) {
     sum += num;
    }
    sum === 15;

    // good
    let sum = 0;
    numbers.forEach((num) => {
     sum += num;
    });

    sum === 15;

有人可以解释一下,为什么forEach优先于常规for循环?它如何真正有所作为?使用常规iterators是否有任何副作用?

6 个答案:

答案 0 :(得分:4)

这没有任何意义。永远不应该首选forEach。是的,使用mapreduce以及filter比使用副作用操纵某些内容的循环更清晰。

但是当您因某种原因需要使用副作用时,for … infor … of是惯用的循环结构。它们更容易阅读和just as fast,并强调你有一个带有副作用的循环体,与forEach形成鲜明对比,它看起来与回调有关,但却没有。与forEach相比的其他优点包括您可以对迭代元素使用const,并且可以使用任意控制结构(returnbreakcontinue,{{ 3}},yield)在循环体中。

答案 1 :(得分:4)

Airbnb样式指南中的这种推理适用于用于不变性的数组方法,filtermapreduce等,但不是forEach:< / p>

  

这会强制执行我们的不可变规则。处理返回值的纯函数比副作用更容易推理。

所以比较更像是:

// bad
let sum = 0;
for (let num of numbers) {
 sum += num;
}
sum === 15;

// bad
let sum = 0;
numbers.forEach((num) => {
 sum += num;
});

sum === 15;

// good
const sum = numbers.reduce((num, sum) => sum += num, 0);

sum === 15;

一般来说,for > forEach > for..of > for..in就性能而言。这种关系几乎在所有引擎中都是一致的,但可能因阵列长度不同而不同

forEach是在最新Chrome / V8中得到显着改进的版本(几乎两倍,基于this综合测试):

由于所有这些都很快,因为性能原因选择不太合适的循环方法可以被认为是初步优化,除非另有证明。

forEach相比,for..of的主要好处是,前者即使在ES3中也可以填充并提供值和索引,而后者更易读,但应在ES5及更低版本中进行转换

forEach已知存在一些陷阱,使其无法在某些可以使用forfor..of正确处理的情况下使用:

  • 回调函数创建新的上下文(可以使用箭头函数解决)

  • 不支持迭代器

  • 不支持生成器yieldasync..await

  • 没有提供使用break提早终止循环的正确方法

答案 2 :(得分:1)

大多数时候,Airbnb风格指南试图保持一致。这并不意味着永远不会有使用for循环或for-in循环的理由,比如假设你想要提前摆脱循环。

当使用for循环来改变某些东西的价值时,不变性就会发挥作用。就像将数组缩减为整数或映射元素以生成新数组一样。使用内置的mapreducefilter,不会直接改变数组的值,因此它是首选的。在for / for-in循环上使用forEach强制了在迭代器上使用更高阶函数的样式一致性,因此我认为这就是为什么它被推荐。

答案 3 :(得分:0)

以下是我在两者之间看到的优势列表。

“ for of”的优点:

  1. 在开发工具中,只需将鼠标悬停即可查看函数中所有变量的值。 (对于forEach,您需要更改您所在的堆栈条目,以查看循环函数中未访问的变量)
  2. 适用于所有可迭代对象(不仅仅是数组)。
  3. 您可以直接使用breakcontinue,而不需要使用some函数并执行return true,这会降低可读性。 (可以为emulated
  4. 您可以在循环内使用await,并保留async / await上下文。 (可以为emulated
  5. 循环内的代码可以直接从整个函数中返回。 (可以为emulated

“ forEach”的优势

  1. 这是mapfilter之类的函数调用,因此在两者之间进行转换更容易。
  2. 在es2015之前的环境中不需要使用转译(只需要一个简单的polyfill)。这也意味着您不必处理编译循环中的奇怪“错误捕获”,这使得在开发工具中进行调试更加困难。
  3. 您可以使用附加功能创建“ custom forEach”方法。 (here是增加了对breakcontinuereturn甚至async / await的支持的版本)
  4. 有点短。 (如果使用for-of,{const多5个字符;如果使用let / var,则多3个字符)

答案 4 :(得分:0)

forEach 迭代不能被await延迟。这意味着您不能使用forEach来管道传输Web请求到服务器。

forEach上也没有async Iterables

请考虑以下情况:

1

let delayed = (value)=>new Promise(resolve => setTimeout(() => resolve(value), 2000));


(async ()=>{
    for(el of ['d','e','f'])console.log(await delayed(el))
    
})();


(async()=>{
    
    ['a','b','c'].forEach(async (el)=>console.log(await delayed(el)))
   
})();

结果:

d
a
b
c
e
f

每两秒钟打印一次[d,e,f]数组的元素。

[a,b,c]数组的元素在两秒钟后全部一起打印

2

let delayed = (value)=>new Promise(resolve => setTimeout(() => resolve(value), 2000));

async function* toDelayedIterable(array) {
    for(a of array)yield (await delayed(a))    
}


for await(el of toDelayedIterable(['d','e','f']))console.log(el)

toDelayedIterable(['a', 'b', 'c']).forEach(async(el)=>console.log(await el));

结果:

d
e
f
Uncaught TypeError: toDelayedIterable(...).forEach is not a function

[d,e,f]数组的元素每两秒钟打印一次

尝试访问forEach的{​​{1}}数组上的asyncIterator时出错。

答案 5 :(得分:-1)

请参阅the tooltip for area此处

仅用于readablity我们正在使用foreach。 除了for循环之外,首选浏览器的兼容性,性能和原生感。