“锁定” JavaScript对象是否对性能有好处?


76

JavaScript 1.8.5(ECMAScript 5)添加了一些有趣的方法,这些方法可以防止以后以某种程度的彻底性修改传递的对象:

大概这些要点是捕捉错误:如果您知道不想在某个点之后修改对象,则可以将其锁定,以便在以后无意间修改它时会引发错误。("use strict";证明您已经做到了。)

我的问题:在使用V8之类的现代JS引擎中,使用上述方法锁定对象是否有任何性能优势(例如,更快的属性查找,减少的内存占用)?

(另请参阅John Resig的一个很好的解释-但是没有提到性能。)


Answers:


82

还有的是没有什么区别,因为至少铬47.0.2526.80(64位)的性能。

Testing in Chrome 6.0.3359 on Mac OS 10.13.4
-----------------------------------------------
Test               Ops/sec
non-frozen object  106,825,468  ±1.08%  fastest
frozen object      106,176,323  ±1.04%  fastest

性能测试(可从http://jsperf.com/performance-frozen-object获得):

  const o1 = {a: 1};
  const o2 = {a: 1};

  Object.freeze(o2);

  // Non-frozen object:
  for(var key in o1);

  // Frozen object:
  for(var key in o2);

更新30.10.2019:Chrome 78.0.3904(64位)的性能没有差异

2019年917日更新:Chrome 76.0.3809(64位)的性能没有差异

更新03.05.2018:Chrome 66.0.3359(64位)的性能没有差异

更新06.03.2017:Chrome 56.0.2924(64位)的性能没有差异

2015年12月13日更新:Chrome 47.0.2526.80(64位)的性能没有差异


使用Chrome 34,在@pimvdb的测试用例中,冻结对象的性能要比未冻结对象好一些(结果如下)。但是,差异似乎还不足以证明使用此技术可获得性能上的好处。

http://jsperf.com/performance-frozen-object

Testing in Chrome 34.0.1847.116 on OS X 10.9.2
----------------------------------------------
Test               Ops/sec
non-frozen object  105,250,353  ±0.41%  3% slower
frozen object      108,188,527  ±0.55%  fastest

运行@kangax的测试用例表明,该对象的两个版本的性能几乎相同:

http://jsperf.com/performance-frozen-object-prop-access

Testing in Chrome 34.0.1847.116 on OS X 10.9.2
----------------------------------------------
Test               Ops/sec
non-frozen object  832,133,923  ±0.26%  fastest
frozen object      832,501,726  ±0.28%  fastest

http://jsperf.com/http-jsperf-com-performance-frozen-object-instanceof

Testing in Chrome 34.0.1847.116 on OS X 10.9.2
----------------------------------------------
Test               Ops/sec
non-frozen object  378,464,917  ±0.42%  fastest
frozen object      378,705,082  ±0.24%  fastest

您的答案是好的,我+1了,但是您应该编辑不建议使用的答案才能正确执行操作。
Nicocube 2014年

感谢您的反馈,@ Nicocube。我不确定编辑不赞成使用的答案还是编写一个新答案是否更好。我已经看到了这两种方法都用于stackoverflow,但是您的建议很有意义。
Jan Molak 2014年

您与jsperf的链接被破坏了something went wrong,您是否拥有为要求声明测试的代码的副本?
Ferrybig

jsperf似乎有问题,因为没有其他指向它们的链接也起作用。当它们重新联机时,我将尝试在此处发布示例。
Jan Molak '18

2
我遇到了这样的情况,即Object.freeze()本身对性能的影响是负面的,因为我有许多不同的小对象(想一棵大树的节点)。结果证明该结构太重,因此我删除了Object.freeze()。
nalply

14

更新:自最初编写此答案以来,已解决了导致此问题的V8中的错误。有关更多信息,请参见Jan Molak的答案


在Google Chrome浏览器(即V8)中,冻结对象的迭代速度比常规对象慢98%

http://jsperf.com/performance-frozen-object

Test name*              ops/sec

non-frozen object    32,193,471
frozen object           592,726

可能是因为这些功能是相对较新的功能,可能尚未进行优化(但这只是我的猜测,老实说,我不知道原因)。

无论如何,我真的不建议将其用于性能上的好处,因为这显然没有意义。


*测试代码为:

var o1 = {a: 1};
var o2 = {a: 1};

Object.freeze(o2);

测试1(非冻结对象):

for(var key in o1);

测试2(冻结对象):

for(var key in o2);

7
这听起来像是V8错误,而不是未优化的错误。注意Object.keys仅慢72%
Raynos 2011年

3
@Raynos:好点;也不Object.keys慢一些。我同意它更像是一个错误,因为结霜不应该影响性能。相反。
pimvdb 2011年

3
有趣的发现。我检查了其他浏览器-每晚FF,WebKit,Opera,但它们都没有如此疯狂的速度下降(请参阅jsperf)。绝对看起来像个错误。我在V8跟踪提起的一个问题- code.google.com/p/v8/issues/detail?id=1858
kangax

20
现在(在Chrome 34中,也就是在2014年),冻结对象的迭代似乎可以加快大约24%的迭代速度。
msung 2014年

7
现在这已经过时了,令人沮丧。
Emil Eriksson 2014年

13

从理论上讲,冻结对象可以使您对对象的形状做出更强有力的保证。

这意味着VM可以压缩内存大小。

这意味着VM可以优化原型链中的属性查找。

这意味着由于对象无法再更改,所有活动引用都变得不活动。

实际上,JavaScript引擎还没有进行这些积极的优化。


1
实际上,在大多数引擎中,对于任何给定的对象,从内存的角度来看,几乎没有什么收获。同样,已经缓存了原型的属性查找(如果没有,大多数内置函数的性能将很糟糕)。
gsnedders,2011年

对。您应该不仅可以拥有内联缓存,还可以内联整个缓存,因为您拥有已知的值。
gsnedders '12

(请注意,内联只会获得一定的数量:例如,您不想内联字符串,尽管内联整数/双精度是您想要做的事情。)
gsnedders 2011年

不幸的是,@ Raynos我们仍然可以修改对象的原型,即使它已经被冻结。获得真正稳定形状的一种方法是将原型设置为null。另一个是冻结整个原型链,以及Object.prototype(听起来很吓人)。
tomekwi

“这意味着所有实时引用都不再生效,因为对象无法再更改”-我认为这对引用的实时意义是一种误解。不能修改的参考仍然可以使用。
davmac '16


2

如果您对对象创建的性能(文字,冻结,密封Immutable.Map,密封)感兴趣,我已经在jsPerf上创建了一个测试来检查这一点。

到目前为止,我只有机会在Chrome 41和Firefox 37中对其进行测试。在这两种浏览器中,冻结或密封对象的创建时间比文字创建时间长三倍,而Immutable.Map性能要比文字创建时间长约50倍。文字。


FWIW,3倍罚款比我预期的要好。除了对性能至关重要的情况外,我将愉快地使用冻结的对象。
tomekwi

如果花费的时间是原来的3倍,那真是太糟糕了……根据链接的jsperf所说,这个性能错误仍然存​​在(Chrome 49)。编译器应将Object.freeze(其中包含文字)作为冻结对象的意图。也就是说,不必两个单独的步骤,一个是对象创建步骤,另一个是冻结步骤。创建(和访问)冻结的对象应该相同或更快。看起来值得一份问题报告。
罗伯特·蒙菲拉,2016年

我可以确认。在JavaScript中实现不可变数据结构时,冻结内部节点会大大降低速度。当您到处都创建了许多新对象时,这是一个不好的选择。最后,对不可变数据结构进行的所有路径复制加起来太多。在我看来,这大约是2倍至3倍,但2倍而不是3倍。
约翰·莱德格伦

-1

我在生产代码中看到这些方法的唯一原因是,出于完整性目的,您可以具有密封或冻结的对象。

例如,我编写了一个小库,该库工作得很好,并在一个对象中为您提供了一组方法,但是我不想让您更改或覆盖我的任何属性或方法。我并不是说我可以阻止您这样做,但是我可以尝试防止您偶然地这样做,这也许更为重要。

而且,这些方法很容易在不知道它们的环境中“填充”,只需返回原始对象即可。那当然就没有效果了。

我看不到任何与性能相关的原因。


1
诚信是为了发展
Raynos

@Raynos:我仍然看到图书馆的目的。正如我所说,更有可能保护对象的完整性免受意外更改。
jAndy 2011年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.