【问题标题】:Should I avoid using object spread in reduce?我应该避免在减少中使用对象传播吗?
【发布时间】:2020-03-26 00:33:39
【问题描述】:

举个例子:

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


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

在这种情况下,是否有使用对象扩展语法的最佳实践赞成或反对?

【问题讨论】:

  • 每次都创建一个具有所有属性的新对象听起来更耗费资源(并且可能不会因任何抖动而优化)。但是,这仅在此操作成为瓶颈时才重要。
  • 更好的选择是使用更合适的语言功能,例如new Map(items.map(x => [x.id, x.name]))

标签: javascript object reduce spread-syntax


【解决方案1】:

出于性能原因,选项 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 可能看起来更像是函数式编程风格,但如果您使用突变来实现您想要的结果,那么您并没有真正进行函数式编程。

请参阅this article,以更深入地讨论为什么选项 1 是一种反模式。

【讨论】:

  • “如果你使用突变来达到你想要的结果” - 你能解释一下吗?你的意思是items的突变?
  • @Stuart valuesreduce 函数中的突变。选项 1 和选项 2 的风格区别在于,选项 1 不会改变累加器,而是创建一个新的累加器,这符合函数式风格。选项 2 改变了累加器,因此它不是真正的函数式样式。链接文章中的“功能纯度”标题下有更多讨论。
【解决方案2】:

在第一个代码中,您将为.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 是一个相对较新的功能,因此如果这适用于面向公众的网站,请确保包含一个 polyfill。

【讨论】:

    【解决方案3】:

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

    【讨论】:

      猜你喜欢
      • 2018-06-16
      • 1970-01-01
      • 2011-03-26
      • 2012-10-14
      • 2010-09-23
      • 2011-12-31
      • 2021-06-03
      • 1970-01-01
      • 2015-04-16
      相关资源
      最近更新 更多