我应该避免在减少中使用对象传播吗?

Posted

技术标签:

【中文标题】我应该避免在减少中使用对象传播吗?【英文标题】: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])) 【参考方案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 每次都会为你的数组创建一个浅拷贝,如果数组很大,这可能会很昂贵。另一方面,在累加器上设置属性更有效。话虽如此,您可以确定您的数组肯定足够小,以至于您更喜欢展开语法的简洁性。

【讨论】:

以上是关于我应该避免在减少中使用对象传播吗?的主要内容,如果未能解决你的问题,请参考以下文章

传播解构以避免重复

我应该避免在 Laravel 项目中使用数据库视图吗?

React - 在不使用 setState 的情况下更改状态:必须避免吗?

Android MVP - 应该避免在演示者中使用 R.string 引用吗?

我应该避免在查询大表时使用 ORDER BY 吗?

我应该避免在 iPhone 上递归吗?