Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript setting object properties with value type in the prototype? [closed]

I always have set properties with a value type in the prototype of the object as this stops them having to be initialized every time the object is created.

Since then I have done some debugging and have found that if the value of that prototype property is ever going to change, then a new property is assigned to the object and the prototype one stays put.

var o = function () {

};

o.prototype.x = 0;

o.prototype.setX = function(x) {
    this.x = x;
};

var obj = new o();
obj.setX(1);
console.log(obj);

// o {x: 1, x: 0, setX: function}

So my question is if you know that the value of the prototype property is likely to change then is there any real gain in setting it in the prototype and benefiting from it not having to be initialized on object creation?

What I mean is when it changes to another value, the new value has to be assigned anyway to the existing object losing the initial benefit of setting it in the prototype in the first place! Furthermore it now means that you have 2 properties for the same thing one in the object and one in the prototype.

It says on google developers that this is the way to do it, but I am not so sure.

When should value type properties be set in the prototype and is there any real performance gain when you know the value is going to change anyway?

like image 281
GriffLab Avatar asked Apr 16 '13 21:04

GriffLab


People also ask

What is difference between __ proto __ and prototype?

prototype is a property of a Function object. It is the prototype of objects constructed by that function. __proto__ is an internal property of an object, pointing to its prototype. Current standards provide an equivalent Object.

What does __ proto __ mean in JavaScript?

__proto__ is a way to inherit properties from an object in JavaScript. __proto__ a property of Object. prototype is an accessor property that exposes the [[Prototype]] of the object through which it is accessed. POSTly is a web-based API tool that allows for fast testing of your APIs (REST, GraphQL).


1 Answers

I've actually benchmarked this sort of thing, and if memory serves, the prototypal model is usually slower than assigning values directly to an object. The exception would be in cases where instances are largely identical and instantiating occurs far more frequently than property access.

So, if you define your objects like this:

var Class = function() {};
Class.prototype.p1 = 1;
Class.prototype.p2 = 1;
Class.prototype.p3 = 1;

You avoid a performance hit at instantiation time, copying the properties to each object. But, That performance shows its head when those properties are accessed or modified. And, if you happen to access those properties without modifying them, you incur the performance hit of walking the prototype chain every time you access them (because they're never copied to the local instance). But, if you have many properties and only access a small handful of them for each instance, this is probably ideal.

for (var i = 0; i < 100000; i++) {
  var x = new Class();
  console.log(x.p1);
  // ignore p2-p99. we don't need them right now.
}

On the other end of the spectrum, if you need to iterate over the properties in a small handful of instances many times, you're better off avoiding the hit of walking the prototype chain.

var Class = function() {
  this.p1 = 1;
  this.p2 = 1;
  this.p3 = 1;
}

Each instance gets its own copy of p1, p2, and p3 at creation time. And the prototype chain doesn't need to be consulted when we access any of them.

var instances = [
  new Class(), new Class(), new Class()
];

for (var i = 0; i < 1000000; i++) {
  console.log(instances[i % instances.length].p1);
  console.log(instances[i % instances.length].p2);
  console.log(instances[i % instances.length].p3);
}

If I have time later, I'll benchmark this later to verify. Until then, all I can give you is the theory!

ADDENDUM

There are two not-necessarily-performance related benefits to using the prototype.

One, for relatively static properties (like functions), it conserves memory. Most applications don't bump up against any limits. But, on the off-change this is an issue for you, use the prototype.

Two, the prototype allows you to assign functions and properties to all existing instances of a class. To accomplish the same feat with instance-level properties, you'd need to find and iterate.

BENCHMARKS

Per my latest benchmarks, which I've only run on the latest versions of FF and Chrome for OS X, I used the following syntax for single-layer (non-inherited) class definitions:

prototype.*

function Class() {}
Class.prototype.a = 1;
Class.prototype.b = 2;
Class.prototype.c = 3;

this.*

function Class() {
  this.a = 1;
  this.b = 2;
  this.c = 3;
}

Between the above two syntaxes, the this.* syntax operates about 10.5% faster all-around.

Adding a level of inheritance, I used the following:

prototype.*

function Base() {}
Base.prototype.a = 1;
Base.prototype.b = 2;
Base.prototype.c = 3;

function Class() {}
Class.prototype = new Base();

this.*

function Base() {
  this.a = 1;
  this.b = 2;
  this.c = 3;
}

function Class() {
  Base.apply(this);
}

And in this case, I found the prototype.* syntax to be about 38.5% faster all-around. The this.* syntax was still slightly faster totalled between browsers for member access; but, the advantage wasn't nearly as noticeable as the instantiation advantage.

I also benchmarked a hybrid approach to inheritance:

function Base() {
  this.a = 1;
  this.b = 2;
  this.c = 3;
}

function Class() {
}
Class.prototype = new Base();

Overall, it ran about 0.5% faster than the prototype.* syntax (probably insignificant). However, it was interestingly about 1% slower during instantiation, but about 2% faster during member access than the prototype.* syntax. Again, not terribly significant, but I can't help wonder whether those gains would scale as inheritance depth increases.

Be aware, of course, that these benchmarks weren't done in a well-controlled setting. I tend to see the noticeably wide gaps in performance as being significant. But, the lower percentages could very well be due to fluctuating CPU load on my machine.

All that said, I would probably advise using the this.* in cases wherein no inheritance is occurring or wherein member access is far more common than class instantiation. And of course, if you're not pressed to squeeze every ounce of performance out of your web app, use the syntax that feels more intuitive to you and your team. Most web-apps will take on much more significant performance hits than the difference in object-building styles.

For instance, changing a background color,

document.body.style.backgroundColor = 'blue';

... is roughly 70% slower than instantiating the worst-performing constructor I benchmarked.

like image 80
svidgen Avatar answered Sep 30 '22 07:09

svidgen