任何性能都有利于“locking”JavaScript对象?
JavaScript 1.8.5(ECMAScript 5)增加了一些有趣的方法, 可以通过不同程度的彻底性来防止对传递对象的未来修改 :
Object.preventExtensions(obj)
-
Object.seal(obj)
-
Object.freeze(obj)
大概这些的主要观点是要发现错误:如果你知道你不想在某个点之后修改一个对象,那么你可以locking它,这样如果你以后不经意修改它,就会抛出一个错误。 (如果你已经完成了"use strict";
那就是)。
我的问题:在V8等现代JS引擎中,使用上述方法locking对象是否有任何性能优势(例如,更快的属性查找,减less的内存占用)?
(另见John Resig的很好的解释 – 虽然没有提到性能。)
在Chrome 47.0.2526.80(64位)上,性能似乎没有差别 。
http://jsperf.com/performance-frozen-object
Testing in Chrome 47.0.2526.80 on OS X 10.9.2 ---------------------------------------------- Test Ops/sec non-frozen object 102,240,853 ±1.02% fastest frozen object 103,982,730 ±1.01% fastest
更新06.03.2017 :在Chrome 56.0.2924(64位)上的性能仍然没有任何区别
使用Chrome 34时,冻结的对象比@ pimvdb的testing用例(下面的结果)中的非冻结对象执行效果要好一些。 然而,这种差异似乎并不足以certificate使用这种技术来获得性能好处。
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的testing用例显示对象的两个版本的performance几乎相同:
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
更新13.12.2015 :添加了Chrome 47.0.2526.80的结果
在谷歌浏览器(所以V8,这是),一个冻结的对象迭代比常规对象慢98% 。
http://jsperf.com/performance-frozen-object
Test name* ops/sec non-frozen object 32,193,471 frozen object 592,726
也许这是因为这些function是相对较新的,可能还没有优化(但这只是我的猜测,我真的不知道原因)。
无论如何,我真的不推荐使用它来获得性能优势,因为这显然没有意义。
*testing的代码是:
var o1 = {a: 1}; var o2 = {a: 1}; Object.freeze(o2);
testing1(非冻结对象):
for(var key in o1);
testing2(冻结对象):
for(var key in o2);
编辑:
由于这个答案是最初编写的, 导致此问题的V8中的错误已得到修复。 请参阅上面的Jan Molak的回答
从理论上讲,冻结一个物体可以让你对物体的形状做出更强的保证。
这意味着虚拟机可以压缩内存大小。
这意味着虚拟机可以优化原型链中的属性查找。
这意味着任何生命引用都不能生存,因为对象不能再改变。
在实践中,JavaScript引擎不会进行这些激进的优化。
V8自2013年6月20日起对Object.freeze进行了优化。截至2014年12月10日的Object.seal和Object.preventExtensions。请参阅https://code.google.com/p/chromium/issues/detail?id= 115960
根据谷歌代码问题 :
性能差异是由于后备存储数据结构。 对于less数属性,对象描述符描述属性数组中的属性存储位置。 如果属性的数量增加,我们最终会切换到一个后备存储的字典,这是一个性能较差,但更灵活。 当我们冻结一个对象时,所做的是所有的属性都被设置为不可configuration的和不可写的。 存储这些属性只能在字典支持存储,所以我们切换到。
编辑:已经做了更多的工作来优化这个和正常的对象和冻结对象之间的差异已经减less到20%左右。 密封的物体仍然需要两倍的时间来迭代,但是工作正在进行。
我在生产代码中看到这些方法的唯一原因是,为了完整性目的,您可以使用密封或冻结的对象。
例如,我写了一个小库,它工作得很好,为对象提供了一套方法,但是我不希望你改变或覆盖我的任何属性或方法。 我并不是说我可以阻止你这样做,但是我可以设法防止你意外这样做,这可能更重要。
而且,这些方法很容易在不了解它们的环境中“回补”,只是返回原来的对象。 当然,那当然没有效果。
我没有看到任何性能相关的原因做到这一点。
如果你对对象创build的性能感兴趣(文字VS冻结vs封闭vs Immutable.Map
),我已经创build了一个testingjsPerf检查出来。
到目前为止,我只有机会在Chrome 41和Firefox 37上进行testing。在这两种浏览器中,冻结或密封对象的创build时间比创build文本要长约三倍 ,而Immutable.Map
执行约50次数比文字差。