我应该避免在 reduce 中使用 object spread 吗?

Should I avoid using object spread in reduce?

给定以下示例:

// option 1
items.reduce((values, item) => ({
     ...values,
     [item.id]: item.name
}), {})


// option 2
items.reduce((values, item) => {
    values[item.id] = item.name;
    return values;
}, {});

在这种情况下,使用对象传播语法是否有赞成或反对的最佳做法?

在第一个代码中,您要为 .reduce 的每次迭代创建一个新对象。在某些引擎中,这可能比您的第二个代码效率稍低,后者仅创建一个对象。 (也就是说,效率很少很重要;在大多数情况下,代码清晰度更为重要)。

但是,对于这种情况,从数组创建对象时有一个更合适的方法,它避免了 reduce:

稍微笨拙的语法
const output = Object.fromEntries(
  items.map(item => [item.id, item])
);

const items = [
  { id: 5, val: 5 },
  { id: 10, val: 10 },
  { id: 15, val: 15 },
];
const output = Object.fromEntries(
  items.map(item => [item.id, item])
);
console.log(output);

也就是说,请记住 Object.fromEntries 是一项相对较新的功能,因此如果这是面向 public 的网站,请确保包含一个 polyfill。

...values 每次都会创建数组的浅表副本,如果数组很大,这可能会很昂贵。另一方面,在累加器上设置 属性 更有效。话虽如此,您可以确定您的数组肯定足够小,以至于您更喜欢简洁的展开语法。

出于性能原因,选项 2 显然更可取:

  • 选项 1 在 O(n²) 时间内运行,因为扩展语法在每次迭代时复制 O(n) 个属性。选项 2 在 O(n) 时间内运行。
  • 选项 1 创建 O(n²) 垃圾,因为它在每次迭代时创建大小为 O(n) 的垃圾对象。选项 2 不会产生垃圾。

就是说,在大多数情况下你应该只用一个普通的旧 for 循环来写这个:

let result = {};
for(let item of items) {
    result[item.id] = item.name;
}

for循环也不错,而且代码比题中的两个选项更具可读性。选项 2 可能看起来更像是函数式编程风格,但如果您使用变异来实现您想要的结果,那么您并不是真正在进行函数式编程。

有关为什么选项 1 是反模式的更深入讨论,请参阅 this article