Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is crockford's JavaScript constructor pattern really supposed to be faster?

I recently tried to optimize some code for an often created value object. (A three dimensional vector, fwiw)

One thing I tried was to convert the constructor function from an anonymous method factory pattern to a normal JavaScript constructor.

This led to a severe performance penalty which surprised me, since the use of 'new' and normal constructors was much recommended in my last question on the subject of JavaScript constructor/factory patterns.

It could well be that my test is too simplistic, or just plain wrong, or a result of recent performance optimizations made in chrome's JavaScript engine, or all of the above. In any case, I'd really like to know why my 'optimizations' led to performance drop - and - most important: Is there any obvious problem with my jsperf testrun?

like image 676
Zaz Avatar asked Jan 02 '13 16:01

Zaz


People also ask

What is correct about constructor in JavaScript?

A constructor is a special function that creates and initializes an object instance of a class. In JavaScript, a constructor gets called when an object is created using the new keyword. The purpose of a constructor is to create a new object and set values for any existing object properties.

Does Prototype improve memory optimization?

'Prototype' helps remove code redundancy which helps boost your app's performance. If you are seeking to optimize resources or memory on your application, you should use prototype .

What happens if you don't use new in JavaScript?

It is NOT 'bad' to use the new keyword. But if you forget it, you will be calling the object constructor as a regular function. If your constructor doesn't check its execution context then it won't notice that 'this' points to different object (ordinarily the global object) instead of the new instance.

Do I need to use new JavaScript?

The new keyword has to be placed before the constructor function call and will do the following things: Creates a new object. Sets the prototype of this object to the constructor function's prototype property. Binds the this keyword to the newly created object and executes the constructor function.


2 Answers

The major differences between your tests are:

  • {} is way faster than new Object, which suggests that new is simply slower than using {}. (The same is true of [] and new Array.)

  • Your tests produce different results: the result of your make factory function isn't a Make object. The constructed Make has a prototype, shared by all Make objects. Your result of your factory function is just a bare Object, and has a single prototype in its prototype chain (Object.prototype), whereas the Make constructed object has two (Make.prototype, followed by Object.prototype).

    I made a fork of your test, with a factory function that actually returns a Make object (instead a simple Object), using the non-standard __proto__ property, and it is much slower than a using a constructor. IE does not support __proto__, but the results from Firefox and Chrome look pretty definitive.

like image 61
apsillers Avatar answered Oct 22 '22 07:10

apsillers


One of the things that a constructor function optimizes for is shared properties, usually methods. If a number of objects use the same functions as methods, or share other named properties, then one assignment to the prototype will share a single instance of the property among all objects created from the constructor, reducing memory overhead, and will not need to repeat the assignment of each such property for every object created, reducing construction time overhead.

Since your sample does not include any such properties, you're not going to see such benefits. But if your production code does not include shared properties for your constructed object, there might well be no reason to switch to a constructor.

So, if, for example you had code like this:

function make(p) {
    return {
        parm: p,
        addTwo: function() {return this.parm + 2;},
        double: function() {return this.parm * 2;},
        square: function() {return this.parm * this.parm;}
    };
};

it will probably run more slowly than this:

function Make(p) {
    this.parm = p;
}
Make.prototype.addTwo = function() {return this.parm + 2;};
Make.prototype.double = function() {return this.parm * 2;}
Make.prototype.square = function() {return this.parm * this.parm;}

It will also take a lot more memory if you create many instances.

like image 22
Scott Sauyet Avatar answered Oct 22 '22 06:10

Scott Sauyet