Javascript prototype operator performance: saves memory, but is it faster?
Asked Answered
M

10

53

I read here (Douglas Crockford) using prototype operator to add methods to Javascript classes saves also memory.

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?

For example, is creating this object:

function Class1()
{
   this.showMsg = function(string) { alert(string); }
}
var c = new Class1();
c.showMsg();

slower than creating 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 objet can 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.

Metalwork answered 16/8, 2010 at 12:52 Comment(7)
Customising an object by adding properties is like handling a special case for this object. It takes more memory and is slower, using the prototype explicitly tells the "compiler" to point on the class definition, sharing the same memory space. Returning explicit object instead of building it from a method can also help the runtime compiler optimise the code (still it it not always possible). More to read here from slide 7Agripinaagrippa
possible duplicate of Defining methods via prototype vs using this in the constructor - really a performance difference?Cormac
Javascript doesn't have classes...Aviles
@AndreasBergström: Class1 is just a name, not a keyword.Metalwork
@MarcoDemaio I know, it just hurts my eyes when I see people talk about classes in JS.Aviles
@Andreas Bergström: JavaScript supports classes in ECMAScript 6.Hordein
@FarazKelhini - The class keyword in ES6 is an abstraction that introduces misdirection in the language to make it feel more like an OO language. To paraphrase the first paragraph of mozilla docs, it sweeps all the internal good parts under the rug so more people will be confused about how JavaScript works. - developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/….Adelric
A
66

Edit in 2021:

This question was asked in 2010 when class was not available in JS. Nowadays, class has been so optimized that there is no excuse not to use it. If you need to use new, use class. But back in 2010 you had two options when binding methods to their object constructors -- one was to bind functions inside the function constructor using this and the other was to bind them outside the constructor using prototype. @MarcoDemaio's question has very concise examples. When class was added to JS, early implementations were close in performance, but usually slower. That's not remotely true anymore. Just use class. I can think of no reason to use prototype today.


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, prototype runs 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):

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 < 10000000; 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 < 10000000; 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.

[Edit] Many thanks to @Kevin who pointed out my previous code was wrong, giving a huge boost to the reported speed of the prototype method. After fixing, prototype is still around significantly faster, but the difference is not as enormous.

Aba answered 16/8, 2010 at 13:46 Comment(19)
I can also verify that this benchmark looks correct… Although I'm curious about the low Safari number. What version are you using? On my machine, I see: FireFox 3.6.8: 5030/377, Safari 5.0: 3037/264.Xeniaxeno
It would also be interesting to see the time breakdown — ie, how much time it takes to instantiate each of X and Y, how much time it takes to look up a property (ex, var tmp = x.message;) and how much time it takes to call a property (ex, x.message('hi')).Xeniaxeno
@David Wolever - My Safari is 5.0.6553.16, which got pushed out fairly recently. I'm on a Mac though. I'm not sure why I don't use Safari more. It's ludicrously fast and the debugger is vastly superior over Firebug, but stupidly I never use it except when I have to. I tried what you suggested. A single property lookup adds about 10% above instantiation-only. Both methods showed roughly the same increase. So there doesn't seem to be any additional cost or savings to using either method. That kind of surprised me. Again, it would be far better to try this with a real-world example.Aba
You could also do: X.prototype = { message: function() { ... }, addition: function() { ... } } Might make it look more "self-encapsulated" to you.Febrifacient
@Andrew: Interesting… I'm on a mac too. And running the tests a few times had the same result. Maybe because I'm running them through the JS console (ie, Develop —> show error console -> paste -> hit enter)?Xeniaxeno
In your first loop you are iterating 1 million times but in your second loop you are only iterating 100 thousand times, so your time comparisons are not apples to apples.Skycap
Thank you, @Kevin. Pretty dumb mistake. I've updated the answer. prototype is still the hands-down winner, but the scale of the win is less impressive. What's strange is how much faster Safari's prototype is compared to Firefox.Aba
FYI: here are the same test on my machine (loop reduced to 100K), but anyway results as absolute values are not comparable, but in percentage terms yes: IE8: Y922ms X593ms; SAFARI4: Y183ms X49ms; FF3.6: Y394ms X16msMetalwork
@Andrew: if you are interested in this link I just found out, you might be ineterested in seeing your test case into: jsperf.comMetalwork
Just ran this on Node.js v0.4.7. The result is about 6x faster for prototype (vs. constructor).Abbey
Am I doing something wrong with the test? y speed: 1000~, x speed: 40~. jsfiddle.net/rN8hp (I believe I found the issue. Chromes JIT compiler is smart enough to see that the message is always the same in the x loop and so it only runs it once)Morea
Updated version that should prevent chrome from removing loop iteration: jsfiddle.net/mP9dS 6x performance with Prototype.Morea
FYI, Firefox is now dramatically faster on the non-prototype, even sometimes beating the prototype version (usually the former is around 150ms, and the latter around 90 ms). Chrome is still slower on the non-prototype version (500-600ms vs. about 30 ms).Mcardle
@Febrifacient "looking self encapsulated" is totally not the same as actually being self encapsulated. One way to use both prototypes and self encapsulation (true privates) is to implement privates with WeakMaps (google it if curious).Lavoie
The benchmark is a bit disingenuous. You're mostly benchmarking the "x = new X()" and "y = new Y()" and NOT the function calls. Iif you move the new outside the loop, then the performance of both methods is almost equal. Here are the results: On FF, with new => 13390 vs 9888, with new outside => 6153 vs 6066. On Chrome (and 10x more iterations), new inside => 3253 vs 1247, with new outside => 311 vs 339.Drought
@OctoPoulos That's what OP was asking. And your observations are exactly what you should expect, even in 2010 when the question was asked. OP's question was specifically this: "...is creating this object (non-prototype example) slower than creating this object (prototype example), then?" It's less obvious now, but in 2010 you created objects with naked constructor functions (see OP's example). If you attached methods inside the constructor (using this) the overhead was huge. Still is. But nowadays, if you need to use new, just use class as it will out-perform function constructors.Aba
"Just use class" is not an answer to the question. Even with class, you can still either create methods as own properties in the constructor function (or class fields), or define them as prototype methods.Cormac
@Cormac Do you have any idea why I'm getting criticized for an answer I wrote 11 years ago? I don't get it at all. "Just use class" was me saying that a (great) 2010 question about .prototype, John Resig and Douglas Crockford has no real value in 2021. I will delete this answer in a day since it is such an obvious irritant to other programmers and doesn't even have any archival value to anyone. A very happy holidays to you.Aba
@Aba I'm only criticising the edit you wrote in September this year, the original answer was fine and still has value. "back in 2010 you had two options" sounds like you no longer have these options, but you still do - it's just different syntax for both with class! And prototype methods are still faster, regardless whether created with class syntax or not.Cormac
O
32

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:

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:

Link

Results:

IE6: closure time: 1062, prototype time: 766, static object time: 406

IE8: closure time: 781, prototype time: 406, static object time: 188

FF: closure time: 233, prototype time: 141, static object time: 94

Safari: closure time: 152, prototype time: 12, static object time: 6

Chrome: closure time: 13, prototype time: 8, static object time: 3

The lesson learned is that if you DON'T have 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.

Orville answered 28/10, 2010 at 9:40 Comment(8)
Well at least a +1 is due here! Thanks for sharing your thought and the test!!! I commonly use static object in JS (and often also in other languages, rather than using a Singleton) when I don't need to instantiate more than one instance of an object. I expected static objet to be faster, but I'm glad you made it clear here with also a test. Thanks again!Metalwork
Nice test, this answer my Javascript design question!Bacterin
Added a test to jsperf.com if anyone is interested: jsperf.com/closure-prototype-static-performanceArty
According to latest test, closure and prototype are almost identical : jsperf.com/closure-prototype-static-performanceConstantine
This test is simply reusing Z over and over again, it is not a fair test, as the others have their new X() and new Y() in the for loop. More fair would be to create new X() once and do the same in the for loop. The test is a bit flawed.Constantine
@momomo's comment is so relevant. Wonder why its not upvoted more. With single instance, closure / prototype are ~ 2X faster than static objects on Chrome 49.xWhiggism
One thing you are NOT testing is how fast it is to heavily USE the object. Yes, the prototype is created faster, but each function call must lookup the prototype chain. The closure object may perform functions faster because of one less scope chain lookup. Or maybe this doesnt matter because the js engine optimizes it for you. I dont know.Winifredwinikka
Donwnvoted because I don't understand how someone can compare creating a million instances versus using the same one a million times.Indonesian
H
6

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:

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.

Heparin answered 13/11, 2012 at 22:51 Comment(2)
I'm not a JavaScript sensei but I created this test for testing performance when accessing and here is it: jsperf.com/accessing-prototyped-and-static-objectsHinckley
The 360 bytes is not fixed -- it depends on how many properties you are setting and what their values are, as well as any optimizations the engine might run.Kroll
P
2

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 access the function. When c.showMsg is referenced, the JavaScript runtime first checks for the property on c. If it's not found, c's prototype is then checked.

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.

Polemic answered 17/8, 2010 at 2:31 Comment(0)
E
2

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.

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.

Essy answered 17/2, 2017 at 6:32 Comment(0)
C
1

I ran my own tests.

The first conclusion is, that static access is actually slower than real prototyping. Interestingly, the Version 23 of this test has 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.

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.

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.

Cyrille answered 25/2, 2015 at 15:30 Comment(0)
A
1

High Resolution Browser Performance API Tests

None of the tests here are taking advantage of the performance API for 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.

Fasted in each category (10,000 iterations)

  • Property access only (~0.5ms): { __proto__: Type }
  • Looping object creation with property access (<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.

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>
Adelric answered 29/8, 2016 at 6:43 Comment(0)
Z
0

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.

But in the end, I would still agree that as a general rules, using prototype will be faster.

Zelda answered 16/8, 2010 at 13:15 Comment(3)
There will be some overhead accessing properties in the prototype, but most engines use a hash table, so adding lots of properties doesn't matter. I think IE is the exception, and does need to loop through each property.Suspicious
Yeas as I was writing this I figured there has to be some optimizations that has been made over the years... so I guess in fact this adds to the fact that adding methods/properties to the object is taking up time cos the map/table would need to be rebuilt/updated ?Zelda
Yes, adding lots of properties would cause the table to be rebuilt eventually.Suspicious
F
0

Funny thing though. It depends not that much on which type of object you create and it matters how you write an example. Likewise i ran similar test as shmuel613 who wrote a similair test as Andrew. The first test is creating a single instance of a constructor, a class and an object literal and then measures the speed of execution from the constructor's instance functions, class's prototype methods and object literal's static functions:

var Y, Z, x, y, z;

class X {
    message(s) {
        var mymessage = s + "";
    };
    addition(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(s) {
        var mymessage = s + "";
    },
    addition(i, j) {
        return (i * 2 + j * 2) / 2;
    }
}

function TestPerformance() {
    console.time("Closure time:");
    y = new Y(); // create a single instance
    for (var i = 0; i < 100000; i++) {
        // I am comparing a single instance with the other single instances
        y.message('hi');
        y.addition(i, 2);
    }
    console.timeEnd("Closure time:");

    console.time("Prototype time:");
    x = new X(); // create a single instance
    for (var i = 0; i < 100000; i++) {
        // I am comparing a single instance with the other single instances
        x.message('hi');
        x.addition(i, 2);
    }
    console.timeEnd("Prototype time:");

    console.time("Static object time:");
    for (var i = 0; i < 100000; i++) {
        z = Z; // obviously you don't really need this
        z.message('hi');
        z.addition(i, 2);
    }
    console.timeEnd("Static object time:");
}

TestPerformance();

The second test measures the speed of execution of creating many instances of a constructor, a class and object literals followed by executing the instance functions, prototype methods and static methods:

var Y, x, y, z;

class X {
    message(s) {
        var mymessage = s + "";
    };
    addition(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;
    };
};

function TestPerformance() {
    console.time("Closure time:");
    //y = new Y()
    for (var i = 0; i < 100000; i++) {
        y = new Y(); // creating an instance
        y.message('hi');
        y.addition(i, 2);
    }
    console.timeEnd("Closure time:");

    console.time("Prototype time:");
    //x = new X();
    for (var i = 0; i < 100000; i++) {
        x = new X(); // creating an instance
        x.message('hi');
        x.addition(i, 2);
    }
    console.timeEnd("Prototype time:");

    console.time("Static object time:");
    for (var i = 0; i < 100000; i++) {
        z = { 
            message(s) {
                var mymessage = s + "";
            },
            addition(i, j) {
                return (i * 2 + j * 2) / 2;
            }
        }; // creating an instance such as from factory functions
        z.message('hi');
        z.addition(i, 2);
    }
    console.timeEnd("Static object time:");
}

TestPerformance();

The lesson learned is that DON'T blindly evolve a prejudice against something without being thorough. The execution speed from instance functions of a constructor (pre ES2016 classes) and the speed from prototype methods of a class are really just as fast as the execution speed from static functions of a object. However the creation speed followed by execution speed of a constructor instance with instance functions versus the creation speed of a class instance with prototype methods versus the creation speed of object literals with static methods shows rather that classes with prototype methods are faster created and executed on Chrome, Microsoft edge, and Opera. The creation speed of an object literal with static methods is only faster at Mozilla firefox

Friend answered 18/5, 2021 at 22:12 Comment(0)
M
-3

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).

Monochrome answered 3/11, 2010 at 9:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.