假设我想对arr中的每个元素求和。

arr = [ { x: 1 }, { x: 2 }, { x: 4 } ];
arr.reduce(function(a, b){ return a.x + b.x; }); // => NaN

我有理由相信a。x在某些时候是没有定义的。

以下工作正常

arr = [ 1, 2, 4 ];
arr.reduce(function(a, b){ return a + b; }); // => 7

第一个例子中我做错了什么?


当前回答

一个更简洁的方法是提供一个初始值作为reduce的第二个参数:

Var arr = [{x:1}, {x:2}, {x:4}]; Var结果= arr。Reduce(函数(acc, obj){返回acc + obj.x;}, 0); console.log(结果);/ / 7

第一次调用匿名函数时,使用(0,{x: 1})调用它,并返回0 + 1 = 1。下一次,它被(1,{x: 2})调用,并返回1 + 2 = 3。然后用(3,{x: 4})调用它,最后返回7。

这也可以处理数组为空的情况,返回0。

其他回答

返回所有x个道具的和:

arr.reduce(
(a,b) => (a.x || a) + b.x 
)

TL;DR,设置初始值

使用解构

加勒比海盗。Reduce ((sum, {x}) => sum + x, 0)

没有解构

加勒比海盗。Reduce ((sum, cur) => sum + cur.x, 0)

与打印稿

加勒比海盗。Reduce ((sum, {x}: {x: number}) => sum + x, 0)

让我们尝试解构方法:

Const arr = [{x: 1}, {x: 2}, {x: 4}] Const result = arr。Reduce ((sum, {x}) => sum + x, 0) Console.log (result) //

关键在于设置初始值。返回值将成为下一次迭代的第一个参数。

上面回答的技巧不是惯用的

接受的答案建议不传递“可选”值。这是错误的,因为惯用的方法是总是包括第二个参数。为什么?三个原因:

1. 危险的 不传递初始值是危险的,如果回调函数不小心,可能会产生副作用和突变。

看哪

const badCallback = (a,i) => Object.assign(a,i) 

const foo = [ { a: 1 }, { b: 2 }, { c: 3 } ]
const bar = foo.reduce( badCallback )  // bad use of Object.assign
// Look, we've tampered with the original array
foo //  [ { a: 1, b: 2, c: 3 }, { b: 2 }, { c: 3 } ]

如果我们这样做,用初始值:

const bar = foo.reduce( badCallback, {})
// foo is still OK
foo // { a: 1, b: 2, c: 3 }

为了记录,除非您打算改变原始对象,否则请设置object的第一个参数。赋值给空对象。像这样:对象。赋值({},a, b, c)

2 -更好的类型推断 当你使用Typescript这样的工具或VS Code这样的编辑器时,你可以告诉编译器初始值,如果你做错了,它可以捕捉到错误。如果您不设置初始值,在许多情况下,它可能无法猜测,最终可能会出现令人毛骨悚然的运行时错误。

3 -尊重函子 当JavaScript内部的子函数被释放出来时,它的表现最好。在函数领域,有一个关于如何“折叠”或缩小数组的标准。当对数组折叠或应用变换时,将获取该数组的值来构造一个新类型。您需要传达结果类型——即使最终类型是数组、另一个数组或任何其他类型中的值的类型,您也应该这样做。

我们换个角度考虑。在JavaScript中,函数可以像数据一样传递,这就是回调的工作方式,下面的代码的结果是什么?

(1、2、3).reduce(回调)

它会返回一个数字吗?一个对象?这样更清楚

[1,2,3]。减少(回调,0)

在这里阅读更多函数式编程规范:https://github.com/fantasyland/fantasy-land#foldable

更多背景信息

reduce方法有两个参数,

Array.prototype.reduce( callback, initialItem )

回调函数接受以下参数

(accumulator, itemInArray, indexInArray, entireArray) => { /* do stuff */ }

在第一次迭代中,

如果提供了initialItem,则reduce函数将initialItem作为累加器传递,并将数组的第一项作为itemInArray传递。 如果没有提供initialItem,则reduce函数将数组中的第一项传递为initialItem,将数组中的第二项传递为itemInArray,这可能会造成混淆。

我教授并建议设置reduce的初始值。

您可以在以下地址查看文档:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce

一个更简洁的方法是提供一个初始值作为reduce的第二个参数:

Var arr = [{x:1}, {x:2}, {x:4}]; Var结果= arr。Reduce(函数(acc, obj){返回acc + obj.x;}, 0); console.log(结果);/ / 7

第一次调用匿名函数时,使用(0,{x: 1})调用它,并返回0 + 1 = 1。下一次,它被(1,{x: 2})调用,并返回1 + 2 = 3。然后用(3,{x: 4})调用它,最后返回7。

这也可以处理数组为空的情况,返回0。

你不应该使用a.x作为累加器,相反,你可以这样做 ' arr = [{x:1},{x:2},{x:4}]

Arr.reduce(函数(a,b){a + b.x},0) '

其他人已经回答了这个问题,但我认为我应该提出另一种方法。你可以结合一个映射(从a.x到x)和reduce(把x相加),而不是直接对a.x求和:

arr = [{x:1},{x:2},{x:4}]
arr.map(function(a) {return a.x;})
   .reduce(function(a,b) {return a + b;});

不可否认,它可能会稍微慢一些,但我认为值得把它作为一个选项提出来。