使用javascript Array reduce()方法有什么好处吗?

Eva*_*You 19 javascript arrays reduce

reduce()方法的大多数用例都可以使用for循环轻松重写.在JSPerf上进行测试表明,reduce()通常会慢60%-75%,具体取决于每次迭代中执行的操作.

除了能够以"功能样式"编写代码之外,还有任何真正的理由使用reduce()吗?如果通过编写更多代码可以获得60%的性能提升,为什么要使用reduce()?

编辑:事实上,其他功能方法如forEach()和map()都表现出类似的性能,比简单的循环慢至少60%.

这是JSPerf测试的链接(带有函数调用):forloop vs forEach

Pav*_*kyi 6

方法的性能可能因数据的大小而异。速度还受编译器优化和数据预热的影响。因此,小数据for of胜出,大数据reduce胜出。

您可以通过运行测试亲眼看到:

const LOOP = 3

test(dataGenerator(5))
test(dataGenerator(500))
test(dataGenerator(50000))
test(dataGenerator(500000))
test(dataGenerator(5000000))

function test(dataSet) {
    let sum

    console.log('Data length:', dataSet.length)

    for (let x = 0; x < LOOP; x++) {
        sum = 0
        console.time(`${x} reduce`)
        sum = dataSet.reduce((s, d) => s += d.data, 0)
        console.timeEnd(`${x} reduce`)
    }

    for (let x = 0; x < LOOP; x++) {
        sum = 0
        console.time(`${x} map`)
        dataSet.map((i) => sum += i.data)
        console.timeEnd(`${x} map`)
    }

    for (let x = 0; x < LOOP; x++) {
        sum = 0
        console.time(`${x} for loop`)
        for (let i = 0; i < dataSet.length; i++) {
            sum += dataSet[i].data
        }
        console.timeEnd(`${x} for loop`)
    }

    for (let x = 0; x < LOOP; x++) {
        sum = 0
        console.time(`${x} for reverse`)
        for (let i = dataSet.length; i--;) {
            sum += dataSet[i].data
        }
        console.timeEnd(`${x} for reverse`)
    }

    for (let x = 0; x < LOOP; x++) {
        sum = 0
        console.time(`${x} for of`)
        for (const item of dataSet) {
            sum += item.data
        }
        console.timeEnd(`${x} for of`)
    }

    for (let x = 0; x < LOOP; x++) {
        sum = 0
        console.time(`${x} for each`)
        dataSet.forEach(element => {
            sum += element.data
        })
        console.timeEnd(`${x} for each`)
    }

    console.log()
}

function dataGenerator(rows) {
    const dataSet = []
    for (let i = 0; i < rows; i++) {
        dataSet.push({id: i, data: Math.floor(100 * Math.random())})
    }
    return dataSet
}
Run Code Online (Sandbox Code Playgroud)

这些是在我的笔记本电脑上进行性能测试的结果。 for loopfor reverse和不同,不能稳定工作for of

?  node reduce_vs_for.js 
Data length: 5
0 reduce: 0.127ms
1 reduce: 0.008ms
2 reduce: 0.006ms
0 map: 0.036ms
1 map: 0.007ms
2 map: 0.018ms
0 for loop: 0.005ms
1 for loop: 0.014ms
2 for loop: 0.004ms
0 for reverse: 0.009ms
1 for reverse: 0.005ms
2 for reverse: 0.004ms
0 for of: 0.008ms
1 for of: 0.004ms
2 for of: 0.004ms
0 for each: 0.046ms
1 for each: 0.003ms
2 for each: 0.003ms

Data length: 500
0 reduce: 0.031ms
1 reduce: 0.027ms
2 reduce: 0.026ms
0 map: 0.039ms
1 map: 0.036ms
2 map: 0.033ms
0 for loop: 0.029ms
1 for loop: 0.028ms
2 for loop: 0.028ms
0 for reverse: 0.027ms
1 for reverse: 0.026ms
2 for reverse: 0.026ms
0 for of: 0.051ms
1 for of: 0.063ms
2 for of: 0.051ms
0 for each: 0.030ms
1 for each: 0.030ms
2 for each: 0.027ms

Data length: 50000
0 reduce: 1.986ms
1 reduce: 1.017ms
2 reduce: 1.017ms
0 map: 2.142ms
1 map: 1.352ms
2 map: 1.310ms
0 for loop: 2.407ms
1 for loop: 12.170ms
2 for loop: 0.246ms
0 for reverse: 0.226ms
1 for reverse: 0.225ms
2 for reverse: 0.223ms
0 for of: 0.217ms
1 for of: 0.213ms
2 for of: 0.215ms
0 for each: 0.391ms
1 for each: 0.409ms
2 for each: 1.020ms

Data length: 500000
0 reduce: 1.920ms
1 reduce: 1.837ms
2 reduce: 1.860ms
0 map: 13.140ms
1 map: 12.762ms
2 map: 14.584ms
0 for loop: 15.325ms
1 for loop: 2.295ms
2 for loop: 2.014ms
0 for reverse: 2.163ms
1 for reverse: 2.138ms
2 for reverse: 2.182ms
0 for of: 1.990ms
1 for of: 2.009ms
2 for of: 2.108ms
0 for each: 2.226ms
1 for each: 2.583ms
2 for each: 2.238ms

Data length: 5000000
0 reduce: 18.763ms
1 reduce: 17.155ms
2 reduce: 26.592ms
0 map: 145.415ms
1 map: 135.946ms
2 map: 144.325ms
0 for loop: 29.273ms
1 for loop: 28.365ms
2 for loop: 21.131ms
0 for reverse: 21.301ms
1 for reverse: 27.779ms
2 for reverse: 29.077ms
0 for of: 19.094ms
1 for of: 19.338ms
2 for of: 26.567ms
0 for each: 22.456ms
1 for each: 26.224ms
2 for each: 20.769ms
Run Code Online (Sandbox Code Playgroud)


nin*_*cko 5

  • 你可能想要范围.例如,您可能想要创建回调函数或引用javascript对象.有关更多信息,请参阅为什么javascript未被阻止作用域. [编辑:现代javascript现在支持let变量.回到ESv6之前,当你声明一个var变量时,它被提升,好像它被写在函数代码块的顶部,因此你经常需要将for循环体写为函数.以下内容仍然适用:]如果您编写了函数,除了它是一个重要的瓶颈之外,还可以使用函数式.
  • 您的代码并不总是需要以完整的机器速度运行.您可能甚至没有优化瓶颈中的代码.
  • 另外,您不提供"JSPerf测试",因此我们可以批评它.例如,如果你已经有一个缩减函数(或map或forEach函数),那么我敢打赌,性能将与之相提并论.即使不是,测试方法也可能存在缺陷,特别是考虑到许多浏览器可能会有不同的优化或具有不同的函数调用开销.

旁注:这是语法之间的有效性能比较,但是当语法不是手头的问题时,性能比较无效:

myArray.map(function(x){return x+1})

// ...versus...

for(var i=0; i<myArray.length; i++) {
    myArray[i] = myArray[i]+1;
}
Run Code Online (Sandbox Code Playgroud)

这将是一个有效的性能比较:

myArray.forEach(function(x){return x+1})

// ...versus...

var plusOne = function(x){return x+1};
for(var i=0; i<myArray.length; i++) {
    plusOne(myArray[i]);
}

// (may need a side-effect if the compiler is smart enough to optimize this)
Run Code Online (Sandbox Code Playgroud)

(同样回复您的编辑:.forEach().map()提供更多清晰度,并避免需要显式循环int i=0; i<array.length; i++参数.)

  • 我不同意你关于什么构成有效的性能比较._first_是有效的比较,因为肯定(对于你的例子)问题是"哪个方法最快添加一个数组的每个元素?",而不是"哪个方法最快,但任何不使用函数调用的方法都被取消资格从条目".我同意如果你认为你想要一个函数来进行范围/闭包,那么你也可以使用`.reduce()`或`.forEach()`或其他什么. (2认同)
  • 在 for 循环中使用 `let` 而不是 `var` 会使您的第一个项目符号在现代 JS 实现中无效(将其留在这里供 2017 年阅读此问题的人使用)。 (2认同)