我应该避免在减少中使用对象传播吗?
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 values
在reduce
函数中的突变。选项 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
每次都会为你的数组创建一个浅拷贝,如果数组很大,这可能会很昂贵。另一方面,在累加器上设置属性更有效。话虽如此,您可以确定您的数组肯定足够小,以至于您更喜欢展开语法的简洁性。
【讨论】:
以上是关于我应该避免在减少中使用对象传播吗?的主要内容,如果未能解决你的问题,请参考以下文章
React - 在不使用 setState 的情况下更改状态:必须避免吗?