例如,有些对象具有相似的结构,但并不完全相同。
const arr = [
{ name: 'john', age: 12 },
{ name: 'marry', age: 24, married: true }
]
或
const obj = {
john: { age: 12 },
marry: { age: 24, married: true }
}
假设约翰没有结婚,所以他不需要married
密钥。(尽管为了一致性,将"已婚"设为false可能更好。(这可能不是一个完美的例子,但无论在哪种情况下,都包括married
密钥并保持对象结构的一致性是否有助于性能?例如,也许它可以帮助CPU更快地将数据写入内存。。。?
抛开标准免责声明不谈(这里所说的一切可能因引擎而异,甚至因同一引擎的不同版本而异,这只是一个实现细节,在优化之前要衡量,在以牺牲可读性为代价进行优化之前要三思而后行,等等等等…(,我认为用谨慎的"是"来回答这个问题是合理的。至少,它不应该伤害,以确保对象始终具有相同的形状。
特别是在V8的情况下,有一个众所周知的隐藏类优化,它根据对象的动态配置内容优化对象的内存布局。运行时对象形状越少,需要跟踪的隐藏类就越少,并且可以在更多情况下重用现有的类。引擎也不必查找多个隐藏类的缓存数据,这可以减少CPU缓存争用。
物体"形状"的构成当然会有所不同,但有许多特征是相关的:
- 存在哪些属性
- 属性添加到对象的顺序(对此的敏感性有时被称为路径依赖性(
- 施工后是否对性能进行了修改
- 保持在这些属性中的值的类型,甚至这些类型的特定子范围(例如,已知V8比其他数字更有效地存储范围[-231,231中的整数,即使JS数字类型名义上覆盖了所有IEEE 754双(
尽管以上内容主要基于V8,但您可以预期任何其他优化JavaScript引擎都会根据对象形状执行大致类似的优化。事实上,即使是相对幼稚的QuickJS也会缓存对象形状以加快属性查找。
在编写代码时,要确保概念上相同"类型"的对象具有一致的形状(例如,让它们总是在代码中的一个地方构造,在那里以相同的顺序添加相同的属性(,大多数时候甚至不应该损害可读性——相反,它甚至可能使代码更容易理解。与其他一些微观优化不同,我认为这一次至少在某种程度上是值得做的。
性能优势包括:
- 几乎可以肯定很小,而且
- 几乎可以肯定的是,JS引擎之间会有所不同
并且不值得担心,除非:
- 您的代码运行速度太慢,并且
- 评测涉及代码中与创建/操作这些对象有关的部分
真正关心的是可维护性。为逻辑相关的对象维护一组一致的属性是一件好事,即使这是不必要的;如果有人想在你的代码上构建,那么如果他们能够为给定的对象集合依赖一组固定的属性,那就太好了。默认为一致地包含额外的属性;如果你的表现得到了提升,那就是肉汁。