Javascript Javascript原型运算符性能:节省内存,但速度更快?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 
原文地址: http://stackoverflow.com/questions/3493252/
Warning: these are provided under cc-by-sa 4.0 license.  You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Javascript prototype operator performance: saves memory, but is it faster?
提问by Marco Demaio
I read here (Douglas Crockford)using prototype operator to add methods to Javascript classes saves also memory.
我在这里阅读(Douglas Crockford)使用原型运算符将方法添加到 Javascript 类也可以节省内存。
Then I read in this John Resig's article"Instantiating a function with a bunch of prototype properties is very, very, fast", but is he talking about using prototype in the standard way, or is he talking about his specific example in his article?
然后我在这篇 John Resig 的文章中读到了“用一堆原型属性实例化一个函数非常、非常、快”,但他是在谈论以标准方式使用原型,还是在他的文章中谈论他的具体例子?
For example, is creating this object:
例如,正在创建这个对象:
function Class1()
{
   this.showMsg = function(string) { alert(string); }
}
var c = new Class1();
c.showMsg();
slower thancreating this object, then?
比创建这个对象还慢,然后呢?
function Class1() {}
Class1.prototype.showMsg = function(string) { alert(string); }
var c = new Class1();
c.showMsg();
P.S.
聚苯乙烯
I know prototype is used to create inheritance and singleton object etc. But this question does not have anyhting to do with these subjects.
我知道原型用于创建继承和单例对象等。但是这个问题与这些主题没有任何关系。
EDIT: to whom it might be interested also in performance comparison between a JS object and a JS static objetcan read this answer below. Static object are definitely faster, obviously they can be usued only when you don't need more than one instance of the object.
编辑:可能对JS 对象和 JS 静态对象之间的性能比较感兴趣的人可以阅读下面的这个答案。静态对象肯定更快,显然只有当您不需要多个对象实例时才能使用它们。
回答by Andrew
It was an interesting question, so I ran some very simple tests (I should have restarted my browsers to clear out the memory, but I didn't; take this for what it's worth). It looks like at least on Safari and Firefox, prototyperuns significantly faster [edit: not 20x as stated earlier]. I'm sure a real-world test with fully-featured objects would be a better comparison. The code I ran was this (I ran the tests several times, separately):
这是一个有趣的问题,所以我运行了一些非常简单的测试(我应该重新启动浏览器以清除内存,但我没有;以它的价值为例)。看起来至少在 Safari 和 Firefox 上,prototype运行速度明显更快 [编辑:不是前面所说的 20 倍]。我确信使用功能齐全的对象进行真实世界的测试会是一个更好的比较。我运行的代码是这样的(我分别运行了几次测试):
var X,Y, x,y, i, intNow;
X = function() {};
X.prototype.message = function(s) { var mymessage = s + "";}
X.prototype.addition = function(i,j) { return (i *2 + j * 2) / 2; }
Y = function() {
    this.message = function(s) { var mymessage = s + "";}
    this.addition = function(i,j) { return (i *2 + j * 2) / 2; }
};
intNow = (new Date()).getTime();
for (i = 0; i < 1000000; i++) {
    y = new Y();
    y.message('hi');
    y.addition(i,2)
}
console.log((new Date()).getTime() - intNow); //FF=5206ms; Safari=1554
intNow = (new Date()).getTime();
for (i = 0; i < 1000000; i++) {
    x = new X();
    x.message('hi');
    x.addition(i,2)
}
console.log((new Date()).getTime() - intNow);//FF=3894ms;Safari=606
It's a real shame, because I really hate using prototype. I like my object code to be self-encapsulated, and not allowed to drift. I guess when speed matters, though, I don't have a choice. Darn. 
这真是一种耻辱,因为我真的很讨厌使用prototype. 我喜欢我的目标代码是自封装的,并且不允许漂移。不过,我想当速度很重要时,我别无选择。该死的。
[Edit] Many thanks to @Kevin who pointed out my previous code was wrong, giving a huge boost to the reported speed of the prototypemethod. After fixing, prototype is still around significantly faster, but the difference is not as enormous.
[编辑] 非常感谢@Kevin,他指出我之前的代码是错误的,大大提高了该prototype方法的报告速度。修复后,原型仍然明显更快,但差异并不大。
回答by shmuel613
I would guess that it depends on the type of object you want to create. I ran a similar test as Andrew, but with a static object, and the static object won hands down. Here's the test:
我猜这取决于您要创建的对象类型。我运行了与 Andrew 类似的测试,但使用的是静态对象,并且静态对象赢得了胜利。这是测试:
var X,Y,Z,x,y,z;
X = function() {};
X.prototype.message = function(s) { var mymessage = s + "";}
X.prototype.addition = function(i,j) { return (i *2 + j * 2) / 2; }
Y = function() {
    this.message = function(s) { var mymessage = s + "";}
    this.addition = function(i,j) { return (i *2 + j * 2) / 2; }
};
Z = {
 message: function(s) { var mymessage = s + "";}
 ,addition: function(i,j) { return (i *2 + j * 2) / 2; }
}
function TestPerformance()
{
  var closureStartDateTime = new Date();
  for (var i = 0; i < 100000; i++)
  {
 y = new Y();
    y.message('hi');
    y.addition(i,2);
  }
  var closureEndDateTime = new Date();
  var prototypeStartDateTime = new Date();
  for (var i = 0; i < 100000; i++)
  {
    x = new X();
    x.message('hi');
    x.addition(i,2);
  }
  var prototypeEndDateTime = new Date();
  var staticObjectStartDateTime = new Date();
  for (var i = 0; i < 100000; i++)
  {
 z = Z; // obviously you don't really need this
    z.message('hi');
    z.addition(i,2);
  }
  var staticObjectEndDateTime = new Date();
  var closureTime = closureEndDateTime.getTime() - closureStartDateTime.getTime();
  var prototypeTime = prototypeEndDateTime.getTime() - prototypeStartDateTime.getTime();
  var staticTime = staticObjectEndDateTime.getTime() - staticObjectStartDateTime.getTime();
  console.log("Closure time: " + closureTime + ", prototype time: " + prototypeTime + ", static object time: " + staticTime);
}
TestPerformance();
This test is a modification of code I found at:
此测试是对我在以下位置找到的代码的修改:
Results:
结果:
IE6: closure time: 1062, prototype time: 766, static object time: 406
IE6:关闭时间:1062,原型时间:766,静态对象时间:406
IE8: closure time: 781, prototype time: 406, static object time: 188
IE8:关闭时间:781,原型时间:406,静态对象时间:188
FF: closure time: 233, prototype time: 141, static object time: 94
FF:关闭时间:233,原型时间:141,静态对象时间:94
Safari: closure time: 152, prototype time: 12, static object time: 6
Safari:关闭时间:152,原型时间:12,静态对象时间:6
Chrome: closure time: 13, prototype time: 8, static object time: 3
Chrome:关闭时间:13,原型时间:8,静态对象时间:3
The lesson learned is that if you DON'Thave a need to instantiate many different objects from the same class, then creating it as a static object wins hands down. So think carefully about what kind of class you really need.
吸取的教训是,如果你不要有一个需要从同一个类实例化许多不同的对象,然后创建它作为一个静态对象手中夺了下来。所以仔细想想你真正需要什么样的课程。
回答by user1822264
So I decided to test this as well. I tested creation time, execution time, and memory use. I used Nodejs v0.8.12 and the mocha test framework running on a Mac Book Pro booted into Windows 7. The 'fast' results are using prototypes and the 'slow' ones are using module pattern. I created 1 million of each type of object and then accessed the 4 methods in each object. Here are the results:
所以我也决定测试一下。我测试了创建时间、执行时间和内存使用。我使用了 Nodejs v0.8.12 和在 Mac Book Pro 上运行的 mocha 测试框架并启动到 Windows 7。“快”的结果是使用原型,而“慢”的结果是使用模块模式。我为每种类型的对象创建了 100 万个,然后访问了每个对象中的 4 个方法。结果如下:
c:\ABoxAbove>mocha test/test_andrew.js
Fast Allocation took:170 msec
·Fast Access took:826 msec
state[0] = First0
Free Memory:5006495744
·Slow Allocation took:999 msec
·Slow Access took:599 msec
state[0] = First0
Free Memory:4639649792
Mem diff:358248k
Mem overhead per obj:366.845952bytes
? 4 tests complete (2.6 seconds)
The code is as follows:
代码如下:
var assert = require("assert"), os = require('os');
function Fast (){}
Fast.prototype = {
    state:"",
    getState:function (){return this.state;},
    setState:function (_state){this.state = _state;},
    name:"",
    getName:function (){return this.name;},
    setName:function (_name){this.name = _name;}
};
function Slow (){
    var state, name;
    return{
        getState:function (){return this.state;},
        setState:function (_state){this.state = _state;},
        getName:function (){return this.name;},
        setName:function (_name){this.name = _name;}
    };
}
describe('test supposed fast prototype', function(){
    var count = 1000000, i, objs = [count], state = "First", name="Test";
    var ts, diff, mem;
    it ('should allocate a bunch of objects quickly', function (done){
        ts = Date.now ();
        for (i = 0; i < count; ++i){objs[i] = new Fast ();}
        diff = Date.now () - ts;
        console.log ("Fast Allocation took:%d msec", diff);
        done ();
    });
    it ('should access a bunch of objects quickly', function (done){
        ts = Date.now ();
        for (i = 0; i < count; ++i){
            objs[i].setState (state + i);
            assert (objs[i].getState () === state + i, "States should be equal");
            objs[i].setName (name + i);
            assert (objs[i].getName () === name + i, "Names should be equal");
        }
        diff = Date.now() - ts;
        console.log ("Fast Access took:%d msec", diff);
        console.log ("state[0] = " + objs[0].getState ());
        mem = os.freemem();
        console.log ("Free Memory:" + mem + "\n");
        done ();
    });
    it ('should allocate a bunch of objects slowly', function (done){
        ts = Date.now ();
        for (i = 0; i < count; ++i){objs[i] = Slow ();}
        diff = Date.now() - ts;
        console.log ("Slow Allocation took:%d msec", diff);
        done ();
    });
    it ('should access a bunch of objects slowly', function (done){
        ts = Date.now ();
        for (i = 0; i < count; ++i){
            objs[i].setState (state + i);
            assert (objs[i].getState () === state + i, "States should be equal");
            objs[i].setName (name + i);
            assert (objs[i].getName () === name + i, "Names should be equal");
        }
        diff = Date.now() - ts;
        console.log ("Slow Access took:%d msec", diff);
        console.log ("state[0] = " + objs[0].getState ());
        var mem2 = os.freemem();
        console.log ("Free Memory:" + mem2 + "\n");
        console.log ("Mem diff:" + (mem - mem2) / 1024 + "k");
        console.log ("Mem overhead per obj:" + (mem - mem2) / count + 'bytes');
        done ();
    });
});
Conclusion: This backs up what others in this post have found. If you are constantly creating objects then the prototype mechanism is clearly faster. If your code spends most of its time accessing objects then the module pattern is faster. If you are sensitive about memory use, the prototype mechanism uses ~360 bytes less per object.
结论:这支持了这篇文章中其他人的发现。如果您不断地创建对象,那么原型机制显然会更快。如果您的代码大部分时间都花在访问对象上,那么模块模式会更快。如果您对内存使用很敏感,原型机制每个对象使用的字节数少约 360 字节。
回答by harto
Intuitively, it seems that it would be more memory-efficient and faster to create functions on the prototype: the function's only created once, not each time a new instance is created.
直观地说,在原型上创建函数似乎更节省内存,速度更快:函数只创建一次,而不是每次创建新实例时。
However, there will be a slight performance difference when it's time to accessthe function. When c.showMsgis referenced, the JavaScript runtime first checks for the property on c. If it's not found, c's prototype is then checked.
但是,当需要访问该函数时,性能会略有不同。当c.showMsg被引用时,JavaScript 运行时首先检查 上的属性c。如果未找到,c则检查 的原型。
So, creating the property on the instance would result in slightly faster access time - but this might only be an issue for a very deep prototype hierarchy.
因此,在实例上创建属性会导致访问时间略快 - 但这可能只是一个非常深的原型层次结构的问题。
回答by Vakhtang
We need to separate object construction and usage.
我们需要将对象的构造和使用分开。
When declaring a function on a prototype, it is shared between all instances. When declaring a function in a constructor, this is recreated every time new instance is made. Given that, we need to benchmark construction and usage separately to have better results. That is what I did and want to share the results with you. This benchmark does not test for speed of construction.
在原型上声明函数时,它在所有实例之间共享。在构造函数中声明函数时,每次创建新实例时都会重新创建。鉴于此,我们需要分别对构建和使用进行基准测试以获得更好的结果。这就是我所做的,并希望与您分享结果。此基准测试不测试构建速度。
function ThisFunc() {
    this.value = 0;
    this.increment = function(){
        this.value++;
    }
}
function ProtFunc() {
    this.value = 0;
}
ProtFunc.prototype.increment = function (){
    this.value++;
}
function ClosFunc() {
    var value = 0;
    return {
        increment:function(){
            value++;
        }
    };
}
var thisInstance = new ThisFunc;
var iterations = 1000000;
var intNow = (new Date()).getTime();
for (i = 0; i < iterations; i++) {
    thisInstance.increment();
}
console.log(`ThisFunc: ${(new Date()).getTime() - intNow}`); // 27ms node v4.6.0
var protInstance = new ProtFunc;
intNow = (new Date()).getTime();
for (i = 0; i < iterations; i++) {
    protInstance.increment();
}
console.log(`ProtFunc: ${(new Date()).getTime() - intNow}`); // 4ms node v4.6.0
var closInstance = ClosFunc();
intNow = (new Date()).getTime();
for (i = 0; i < iterations; i++) {
    closInstance.increment();
}
console.log(`ClosFunc: ${(new Date()).getTime() - intNow}`); // 7ms node v4.6.0
From these results we can see that the prototype version is the fastest (4ms), but the closure version is very close (7ms). You may still need to benchmark for your particular case.
从这些结果我们可以看出,原型版本最快(4ms),但闭包版本非常接近(7ms)。您可能仍需要针对您的特定情况进行基准测试。
So:
所以:
- We can use prototype version when we need to have every bit of performance or share functions between instances.
- We can use other versions when what we want is the features they provide. (private state encapsulation, readability etc.)
- 当我们需要在实例之间拥有所有性能或共享功能时,我们可以使用原型版本。
- 当我们想要的是他们提供的功能时,我们可以使用其他版本。(私有状态封装,可读性等)
PS: I used Andrew's answer as a reference. Used the same loops and notation.
PS:我用安德鲁的回答作为参考。使用相同的循环和符号。
回答by Benno
The first conclusion is, that static access is actually slower than real prototyping. Interestingly, the Version 23 of this testhas a flawed prototyping (Variable X) in it, which just returns the completely overridden prototype object over and over again and when I was creating my test, this prototyping was still slower than my "real prototype" test.
第一个结论是,静态访问实际上比真正的原型设计慢。有趣的是,这个测试的第 23 版有一个有缺陷的原型(变量 X),它一遍又一遍地返回完全覆盖的原型对象,当我创建我的测试时,这个原型仍然比我的“真实原型”慢测试。
Anyway, to the answer: Unless my test is flawed, it shows that real prototyping is fastest. It beats or is at least equal to the static object when ignoring instantiation. this-assignments on instantiation and private variables are both much slower. I wouldn't have guessed private variables would be this slow.
无论如何,答案是:除非我的测试有缺陷,否则它表明真正的原型设计是最快的。在忽略实例化时,它击败或至少等于静态对象。实例化和私有变量上的 this-assignments 都慢得多。我没想到私有变量会这么慢。
It might be of interest that I extended the prototype Object with jQuery.extend in between and it was about the same speed as the direct assignment. The extend was outside the test itself, of course. At least this is a way to circumvent writing annoying ".prototype."-Parts all the time.
我可能会感兴趣的是,我使用 jQuery.extend 扩展了原型对象,它与直接赋值的速度大致相同。当然,扩展超出了测试本身。至少这是一种避免编写烦人的“.prototype”的方法。 - 一直都是零件。
回答by cchamberlain
High Resolution Browser Performance API Tests
高分辨率浏览器性能 API 测试
None of the tests here are taking advantage of the performance APIfor high resolution testing so I wrote one that will show current fastest results for many different scenarios including 2 that are faster than any of the other answers on most runs.
这里的测试都没有利用性能 API进行高分辨率测试,因此我编写了一个测试,它将显示许多不同场景的当前最快结果,包括在大多数运行中比任何其他答案都快的 2。
Fasted in each category (10,000 iterations)
在每个类别中禁食(10,000 次迭代)
- Property access only (~0.5ms):  { __proto__: Type }
- Looping object creation with property access (<3ms):  Object.create(Type)
- 仅属性访问(~0.5ms):  { __proto__: Type }
- 使用属性访问(<3ms)循环创建对象:  Object.create(Type)
The code uses ES6 without babel transpilation to ensure accuracy. It works in current chrome. Run the test below to see the breakdown.
代码使用 ES6,没有 babel 转译以确保准确性。它适用于当前的 chrome。运行下面的测试以查看故障。
function profile () {
  function test ( name
                , define
                , construct
                , { index = 0
                  , count = 10000
                  , ordinals = [ 0, 1 ]
                  , constructPrior = false
                  } = {}
                ) {
    performance.clearMarks()
    performance.clearMeasures()
    const symbols = { type: Symbol('type') }
    const marks = (
      { __proto__: null
      , start: `${name}_start`
      , define: `${name}_define`
      , construct: `${name}_construct`
      , end: `${name}_end`
      }
    )
    performance.mark(marks.start)
    let Type = define()
    performance.mark(marks.define)
    let obj = constructPrior ? construct(Type) : null
    do {
      if(!constructPrior)
        obj = construct(Type)
      if(index === 0)
        performance.mark(marks.construct)
      const measureOrdinal = ordinals.includes(index)
      if(measureOrdinal)
          performance.mark(`${name}_ordinal_${index}_pre`)
      obj.message('hi')
      obj.addition(index, 2)
      if(measureOrdinal)
        performance.mark(`${name}_ordinal_${index}_post`)
    } while (++index < count)
    performance.mark(marks.end)
    const measureMarks = Object.assign (
      { [`${name}_define`]: [ marks.start, marks.define ]
      , [`${name}_construct`]: [ marks.define, marks.construct ]
      , [`${name}_loop`]: [ marks.construct, marks.end ]
      , [`${name}_total`]: [ marks.start, marks.end ]
      }
    , ordinals.reduce((reduction, i) => Object.assign(reduction, { [`${name}_ordinal_${i}`]: [ `${name}_ordinal_${i}_pre`, `${name}_ordinal_${i}_post` ] }), {})
    )
    Object.keys(measureMarks).forEach((key) => performance.measure(key, ...measureMarks[key]))
    const measures = performance.getEntriesByType('measure').map(x => Object.assign(x, { endTime: x.startTime + x.duration }))
    measures.sort((a, b) => a.endTime - b.endTime)
    const durations = measures.reduce((reduction, measure) => Object.assign(reduction, { [measure.name]: measure.duration }), {})
    return (
      { [symbols.type]: 'profile'
      , profile: name
      , duration: durations[`${name}_total`]
      , durations
      , measures
      }
    )
  }
  const refs = (
    { __proto__: null
    , message: function(s) { var mymessage = s + '' }
    , addition: function(i, j) { return (i *2 + j * 2) / 2 }
    }
  )
  const testArgs = [
    [ 'constructor'
    , function define() {
        return function Type () {
          this.message = refs.message
          this.addition = refs.addition
        }
      }
    , function construct(Type) {
        return new Type()
      }
    ]
  , [ 'prototype'
    , function define() {
        function Type () {
        }
        Type.prototype.message = refs.message
        Type.prototype.addition = refs.addition
        return Type
      }
    , function construct(Type) {
        return new Type()
      }
    ]
  , [ 'Object.create'
    , function define() {
        return (
          { __proto__: null
          , message: refs.message
          , addition: refs.addition
          }
        )
      }
    , function construct(Type) {
        return Object.create(Type)
      }
    ]
  , [ 'proto'
    , function define() {
        return (
          { __proto__: null
          , message: refs.message
          , addition: refs.addition
          }
        )
      }
    , function construct(Type) {
        return { __proto__: Type }
      }
    ]
  ]
  return testArgs.reduce(
    (reduction, [ name, ...args ]) => (
      Object.assign( reduction
      , { [name]: (
            { normal: test(name, ...args, { constructPrior: true })
            , reconstruct: test(`${name}_reconstruct`, ...args, { constructPrior: false })
            }
          )
        }
      )
    )
  , {})
}
let profiled = profile()
const breakdown = Object.keys(profiled).reduce((reduction, name) => [ ...reduction, ...Object.keys(profiled[name]).reduce((r, type) => [ ...r, { profile: `${name}_${type}`, duration: profiled[name][type].duration } ], []) ], [])
breakdown.sort((a, b) => a.duration - b.duration)
try {
  const Pre = props => React.createElement('pre', { children: JSON.stringify(props.children, null, 2) })
  
  ReactDOM.render(React.createElement(Pre, { children: { breakdown, profiled } }), document.getElementById('profile'))
} catch(err) {
    console.error(err)
}<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="profile"></div>回答by SBUJOLD
I'm sure that as far as instantiating the object goes, it's way faster and also consumes less memory, no doubts about that, but I would think that the javascript engine needs to loop through all the properties of the object to determine if the property/method invoked is part of that object and if not, then go check for the prototype. I am not 100% sure about this but I'm assuming that's how it works and if so, then in SOME cases where your object has a LOT of methods added to it, instantiated only once and used heavily, then it could possibly be a little slower, but that's just a supposition I haven't tested anything.
我确信就实例化对象而言,它的速度更快,而且消耗的内存更少,这一点毫无疑问,但我认为 javascript 引擎需要遍历对象的所有属性来确定该属性是否/method 调用是该对象的一部分,如果不是,则检查原型。我对此不是 100% 确定,但我假设它是这样工作的,如果是这样,那么在某些情况下,您的对象添加了很多方法,仅实例化一次并大量使用,那么它可能是一个慢一点,但这只是我没有测试过任何东西的假设。
But in the end, I would still agree that as a general rules, using prototype will be faster.
但最后,我仍然同意,作为一般规则,使用原型会更快。
回答by Piotr
So, creating the property on the instance would result in slightly faster access time - but this might only be an issue for a very deep prototype hierarchy.
因此,在实例上创建属性会导致访问时间略快 - 但这可能只是一个非常深的原型层次结构的问题。
Actually the result is different then we could expect - access time to prototyped methods is faster then accessing to the methods attached exactly to the object (FF tested).
实际上结果与我们预期的不同 - 访问原型方法的时间比访问完全附加到对象的方法(FF 测试)更快。

