首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么Object.create比构造函数慢得多?

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

Stack Overflow用户
提问于 2015-12-27 13:10:48
回答 4查看 4.4K关注 0票数 16

背景

在我所维护的一个项目中,我们广泛使用null原型对象作为穷人的替代(仅使用字符串键)地图,在许多旧的、预ES6浏览器中不支持这些映射。

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

代码语言:javascript
复制
var foo = Object.create(null);

这保证了新对象没有继承的属性,例如"toString“、”构造函数“、"__proto__”,这些属性对于这个特定的用例来说是不可取的。

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

代码语言:javascript
复制
var Empty = function () { };
Empty.prototype = Object.create(null);

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

代码语言:javascript
复制
var bar = new Empty;

问题所在

在努力提高性能的过程中,我编写了一个测试,并发现本机Object.create方法在所有浏览器( http://jsperf.com/blank-object-creation )中的执行速度都比包含一个临时原型的额外构造函数的方法要慢得多。

我巧妙地期望后一种方法更慢,因为它涉及调用用户定义的构造函数,在前一种情况下不会发生这种情况。

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

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2016-01-05 12:44:29

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

  • 在Chrome 47中,new Empty以63m ops/秒的速度运行,而Object.create(null)的运行速度为10m ops/秒。
  • 在Firefox39中,new Empty以7.33亿ops/秒的速度运行,而Object.create(null)的运行速度为1,685百万/秒。

(上面的“m”意味着我们谈论的是数百万人。)

那么,您选择哪一个呢?,在一个浏览器中速度最快的方法在另一个浏览器中最慢。

不仅如此,我们在这里看到的结果很可能随着新的浏览器版本而改变。就是一个很好的例子,我已经检查了Object.create在v8中的实现。到2015年12月30日,Object.create的实现是用JavaScript编写的,而提交最近更改了它则是C++的实现。一旦进入Chrome,比较Object.create(null)new Empty的结果就会发生变化。

但这还不是全部..。

--您只看过使用Object.create(null)创建对象的一个方面--,该对象将用作一种映射(伪映射)。进入这个伪地图的访问时间呢?下面是一个检查失手性能的测试和一个检查命中性能的测试。

  • 在Chrome 47上,使用Object.create(null)创建对象时,命中和漏报的速度都快了90%。
  • 在Firefox 39上,命中的情况都是一样的。至于错过的情况,使用Object.create(null)创建的对象的性能非常好,jsperf告诉我操作/秒的数量是“无限”。

Firefox 39所获得的结果正是我所期望的。JavaScript引擎应该在对象本身中查找字段。如果是命中,那么搜索就结束了,不管如何创建对象。如果在对象本身中找不到字段,那么JavaScript引擎必须签入对象的原型。在用Object.create(null)创建对象的情况下,没有原型,所以搜索就在那里结束。对于用new Empty创建的对象,有一个原型,其中JavaScript引擎必须搜索。

现在,在伪映射的生命周期中,创建伪映射的频率是多少?它多久被访问一次?除非您处于一个非常特殊的情况下,否则地图应该创建一次,但是访问很多次。因此,命中和遗漏的相对性能将对应用程序的总体性能更重要,然后是创建对象的各种方法的相对性能。

我们还可以查看从这些伪映射中添加和删除键的性能,我们将学到更多。再说一遍,也许你有地图,你从来不删除键(我有几个),所以删除性能对你的情况可能并不重要。

最终,为了提高应用程序的性能,您应该分析的是作为system.的应用程序,这样,实际应用程序中各种操作的相对重要性将反映在您的结果中。

票数 22
EN

Stack Overflow用户

发布于 2016-01-05 02:04:19

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

尽管如此,所有的性能测试都证明,您不应该基于性能来选择一个或另一个,因为创建对象的成本非常低。这些地图中有多少是你制作的?即使是Object.create在测试中最慢的实现,每秒也会产生超过8,000,000个对象,所以除非您有令人信服的理由创建数百万张地图,否则我只会选择最明显的解决方案。

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

最终,Object.create(null)是显而易见的解决方案。如果创建对象的性能成为瓶颈,那么可能会考虑使用构造函数,但即便如此,在使用类似于Empty构造函数的工具之前,我还是会将目光转向其他地方。

票数 5
EN

Stack Overflow用户

发布于 2016-01-05 12:43:01

这个问题几乎是无效的,因为jsperf是坏的,它会以任何原因导致结果的倾斜。我在创建自己的地图实现(一个基于整数)时亲自检查了它。

这两种方法完全没有区别。

顺便说一句,我认为这是用相同语法创建空对象的一种更简单的方法:

代码语言:javascript
复制
var EmptyV2 = function() { return Object.create(null); };

我编写了自己的小测试,打印时间来创建这3种方法的任何数量。

下面是:

代码语言:javascript
复制
<!DOCTYPE html>
<html>
    <head>
        <style>
            html
            {
                background-color: #111111;
                color: #2ECC40;
            }
        </style>
    </head>
    <body>
    <div id="output">

    </div>

    <script type="text/javascript">
        var Empty = function(){};
        Empty.prototype = Object.create(null);

        var EmptyV2 = function() { return Object.create(null); };

        var objectCreate = Object.create;

        function createEmpties(iterations)
        {           
            for(var i = 0; i < iterations; i++)
            {           
                var empty = new Empty();
            }
        }

        function createEmptiesV2(iterations)
        {       
            for(var i = 0; i < iterations; i++)
            {
                var empty = new EmptyV2();
            }
        }

        function createNullObjects(iterations)
        {       
            for(var i = 0; i < iterations; i++)
            {
                var empty = objectCreate(null);
            }
        }

        function addResult(name, start, end, time)
        {           
            var outputBlock = document.getElementsByClassName("output-block");

            var length = (!outputBlock ? 0 : outputBlock.length) + 1;
            var index = length % 3;

            console.log(length);
            console.log(index);

            var output = document.createElement("div");
            output.setAttribute("class", "output-block");
            output.setAttribute("id", ["output-block-", index].join(''));
            output.innerHTML = ["|", name, "|", " started: ", start, " --- ended: ", end, " --- time: ", time].join('');

            document.getElementById("output").appendChild(output);

            if(!index)
            {
                var hr = document.createElement("hr");
                document.getElementById("output").appendChild(hr);
            }
        }

        function runTest(test, iterations)
        {
            var start = new Date().getTime();

            test(iterations);

            var end = new Date().getTime();

            addResult(test.name, start, end, end - start);
        }

        function runTests(tests, iterations)
        {
            if(!tests.length)
            {
                if(!iterations)
                {
                    return;
                }

                console.log(iterations);

                iterations--;

                original = [createEmpties, createEmptiesV2, createNullObjects];

                var tests = [];

                for(var i = 0; i < original.length; i++)
                {
                    tests.push(original[i]);
                }
            }

            runTest(tests[0], 10000000000/8);

            tests.shift();

            setTimeout(runTests, 100, tests, iterations);
        }

        runTests([], 10);
    </script>
    </body>
</html>

对不起,太僵硬了。只需将其粘贴到index.html中并运行即可。我认为这种测试方法比jsperf好得多。

以下是我的研究结果:

开始: 1451996562280 --结束: 1451996563073 --时间: 793

开始: 1451996563181 --结束: 1451996564033 --时间: 852

开始: 1451996564148 --结束: 1451996564980 --时间: 832

开始: 1451996565085 --结束: 1451996565926 --时间: 841

开始: 1451996566035 --结束: 1451996566863 --时间: 828

开始: 1451996566980 --结束: 1451996567872 --时间: 892

开始: 1451996567986 --结束: 1451996568839 --时间: 853

开始: 1451996568953 --结束: 1451996569786 --时间: 833

开始创建1451996570713 : 1451996569890 --结束:1451996570713--时间: 823

开始: 1451996570825 --结束: 1451996571666 --时间: 841

开始: 1451996571776 --结束: 1451996572615 --时间: 839

开始: 1451996572728 --结束: 1451996573556 --时间: 828

开始: 1451996573665 --结束: 1451996574533 --时间: 868

开始: 1451996574646 --结束: 1451996575476 --时间: 830

开始创建1451996576427 : 1451996575582 --结束:1451996576427--时间: 845

开始: 1451996576535 --结束: 1451996577361 --时间: 826

开始: 1451996577470 --结束: 1451996578317 --时间: 847

开始: 1451996578422 --结束: 1451996579256 --时间: 834

开始: 1451996579358 --结束: 1451996580187 --时间: 829

开始: 1451996580293 --结束: 1451996581148 --时间: 855

开始: 1451996581261 --结束: 1451996582098 --时间: 837

开始: 1451996582213 --结束: 1451996583071 --时间: 858

开始: 1451996583179 --结束: 1451996583991 --时间: 812

开始创建1451996584948 : 1451996584100 --结束:1451996584948--时间: 848

开始: 1451996585052 --结束: 1451996585888 --时间: 836

开始: 1451996586003 --结束: 1451996586839 --时间: 836

开始: 1451996586954 --结束: 1451996587785 --时间: 831

开始: 1451996587891 --结束: 1451996588754 --时间: 863

开始: 1451996588858 --结束: 1451996589702 --时间: 844

开始: 1451996589810 --结束: 1451996590640 --时间: 830

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/34480709

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档