为什么Object.create比构造函数慢得多?

GOT*_*O 0 13 javascript performance constructor object-create

背景

在一个项目中,我维护我们广泛使用null原型对象作为穷人的替代(仅限字符串键)映射,这些映射在许多较旧的ES6之前的浏览器中不是本机支持的.

基本上,要动态创建一个null原型对象,可以使用:

var foo = Object.create(null);
Run Code Online (Sandbox Code Playgroud)

这保证了新对象没有继承属性,例如"toString","constructor","__ proto__",这对于这个特定的用例是不可取的.

由于这种模式在代码中多次出现,我们提出了编写构造函数的想法,该构造函数将创建其原型具有null原型且没有自己的属性的对象.

var Empty = function () { };
Empty.prototype = Object.create(null);
Run Code Online (Sandbox Code Playgroud)

然后,要创建一个没有自己或继承属性的对象,可以使用:

var bar = new Empty;
Run Code Online (Sandbox Code Playgroud)

问题

为了提高性能,我编写了一个测试,并发现在Object.create所有浏览器中,本机方法的意外执行速度比涉及带有ad hoc原型的额外构造函数的方法慢得多:http://jsperf.com/blank-object - 创造.

我非常期待后一种方法更慢,因为它涉及调用用户定义的构造函数,这在前一种情况下不会发生.

造成这种性能差异的原因是什么?

Lou*_*uis 16

您一直在调查一些高度依赖于您运行的浏览器的特定版本的内容.以下是我在运行jsperf测试时得到的一些结果:

  • 在Chrome 47中,new Empty运行速度为63m ops/sec,而Object.create(null)运行速度为10m ops/sec.

  • 在Firefox 39中,new Empty运行速度为733m ops/sec,而Object.create(null)运行速度为1,685m ops/sec.

(上面的"m"表示我们谈论的是数百万.)

那你选哪一个?在一个浏览器中最快的方法在另一个浏览器中最慢.

不仅如此,我们在这里看到的结果很可能随着新的浏览器版本而改变.例如,我已经检查了Object.createv8中的实现.截至2015年12月30日,实现Object.create是用JavaScript编写的,但最近提交将其更改为C++实现.一旦这使得它的方式进入浏览器,比较的结果Object.create(null),并new Empty要改变.

但这并不是全部...

你看只是一个方面使用的Object.create(null)创建将被用作样图(伪地图)的对象.那个伪地图的访问时间怎么样?这是一个检查未命中性能的测试,以及一个检查命中性能的测试.

  • 在Chrome 47上,使用创建对象时,命中和未命中情况的速度提高了90%Object.create(null).

  • 在Firefox 39上,命中案例都执行相同的操作.对于未命中的情况,创建的对象的性能Object.create(null)是如此之大,以至于jsperf告诉我操作数/秒是"无限".

使用Firefox 39获得的结果是我实际期待的结果.JavaScript引擎应该在对象本身中寻找字段.如果它是一个命中,那么无论对象是如何创建的,搜索都会结束.如果在对象本身中找不到字段,则JavaScript引擎必须检入对象的原型.在使用创建的对象的情况下,Object.create(null)没有原型,因此搜索在那里结束.对于使用创建的对象new Empty,有一个原型,JavaScript引擎必须在其中进行搜索.

现在,在伪地图的生命周期中,伪地图的创建频率是多少?它被访问的频率是多少?除非你处于一个非常特殊的情况,否则地图应该创建一次,但是可以多次访问. 因此,命中和未命中的相对性能对应用程序的整体性能更重要,然后是创建对象的各种方法的相对性能.

我们还可以查看从这些伪映射中添加和删除键的性能,我们将了解更多信息.然后,也许你有地图,你从来没有删除密钥(我有一些),所以删除性能可能对你的情况不重要.

最终,您应该为提高应用程序性能而进行分析的是您作为系统的应用程序.通过这种方式,实际应用程序中各种操作的相对重要性将反映在您的结果中.


Eth*_*ynn 5

性能差异与构造函数在大多数 JS 引擎中高度优化的事实有关。Object.create 不能像构造函数一样快确实没有实际原因,它只是一个依赖于实现的东西,随着时间的推移可能会改进。

话虽如此,所有性能测试都证明,您不应该根据性能选择其中之一,因为创建对象的成本低得离谱。您正在创建多少个这些地图?即使在测试中最慢的 Object.create 实现仍然每秒生成超过 8,000,000 个对象,因此除非您有令人信服的理由来创建数百万个地图,否则我只会选择最明显的解决方案。

此外,请考虑这样一个事实,即一个浏览器实现实际上可以比另一个实现快 100 倍。无论您选择哪个,这种差异都会存在,因此 Object.create 和构造函数之间的微小差异不应真正被视为不同实现的更广泛上下文中的相关差异。

最终, Object.create(null) 是显而易见的解决方案。如果创建对象的性能成为瓶颈,那么也许可以考虑使用构造函数,但即便如此,在我诉诸于使用Empty构造函数之类的东西之前,我还是会寻找其他地方。