Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Functions inside constructor vs prototype

Tags:

javascript

I know there are similar questions like this, but I want to see if those answers are still valid given optimizations in new Javascript engines.

In my opinion, the biggest benefit about defining functions inside the constructor is that you can easily avoid having to know the value of 'this' keyword:

var Person = function() {
  var self = this;
  self.firstName = null;
  self.lastName = null;
  self.fullName = function() {
    return self.firstName + self.lastName;
  };
};

This approach is recommended by Knockout Managing 'this'. This is a great advantage, especially when the code is being modified by many developers as it is really simple to understand and use.

The other approach would be to use the object prototype:

var Person = function() {
  this.firstName = null;
  this.lastName = null;
};
Person.prototype.fullName = function() {
  return this.firstName + this.lastName;
};

In this case there are performance advantages because functions objects will be created once. However the main issue I have with it is that it might be complicated to handle the 'this' keyword. The above example is very simple, but if you have event handlers, forEach calls, jQuery each() calls, methods being call from different contexts, etc., it is easy to make a bad use of this.

Of course, if you understand how 'this' works and are aware of how methods are being called, you shouldn't have much issues. However, in my experience, this takes time and it is error prone, especially when code is crafted by many developers.

I know that new JS engines, like V8, are applying optimizations to cases where you declare functions inside the constructor by creating hidden classes: How the V8 engine works?.

So my question is, given these optimizations done by new JS engines and the complexity of having to handle the 'this' keyword, does it still make sense to use the prototype based approach? What I would loose by using the approach of putting everything inside the constructor?

UPDATE 1:

I just did a micro-benchmark on Chrome (version 42). I create 1M objects with functions inside constructor and functions in prototype. It is a very simple object with two variables and three functions and the results are like this:

Functions inside constructor: 1.91 seconds
Functions in prototype: 1.10 seconds

Sounds like even with those optimizations in V8 it is still 73% faster. However this was a micro-benchmark. Not sure if that will be a big difference in real world applications.

UPDATE 2:

I also took a look at memory consumption and there are big differences as well. For functions inside constructors:

Shallow size: 64,000,120
Retained size: 336,001,128

For prototype functions:

Shallow size: 40,000,000
Retained size: 40,000,000

Either optimizations with hidden class are not that good or I'm missing something about that. I'm using monomorphic code (constructors with no args) as suggested by V8, but not sure if I'm doing something wrong.

UPDATE 3:

Here is the link of the test I did in case someone can point out something wrong in there: http://jsperf.com/dg-constructor-vs-prototype

like image 719
dgaviola Avatar asked May 27 '15 15:05

dgaviola


People also ask

Can we define a function inside constructor?

When you define a function inside the constructor as this. myFunction=... , it is specific to your instance. This means that it must be constructed and kept in memory for all instances, which may be heavy. It also can't be inherited .

Can I put a function in constructor?

Let's note once again – technically, any function (except arrow functions, as they don't have this ) can be used as a constructor. It can be run with new , and it will execute the algorithm above. The “capital letter first” is a common agreement, to make it clear that a function is to be run with new .

What is the difference between a constructor and prototype?

So what's the difference between constructor and prototype? A short answer is that the constructor is a function that is used to create an object, while the prototype is an object that contains properties and methods that are inherited by objects created from a constructor.

What is the prototype of a constructor function?

constructor. The constructor property returns a reference to the Object constructor function that created the instance object. Note that the value of this property is a reference to the function itself, not a string containing the function's name.


2 Answers

Like @Ersin Basaran mentioned, a function created inside the constructor is unique for every object instance, unlike when it is created using the prototype makes it the same function for every object instance.

However, after introducing classes in ES6 (ECMAScript2015), if you use a class to create a method instead of using a constructor function, and you create this method outside the constructor (but inside the class), it will be the same for every object instance, just like when using the prototype.

Here is an example of creating a fullName() method:

class Person {
    constructor () {
        var self = this;
        self.firstName = null;
        self.lastName = null;
    }
    fullName () {
        return self.firstName + self.lastName;
    }
}

Person.prototype.fullName2 = function () {
    return this.firstName + this.lastName;
};

var a = new Person();
var b = new Person();

console.log(a.fullName == b.fullName); // returns true
console.log(a.fullName2 == b.fullName2); // returns true

I hope this helps.

like image 163
Fouad Boukredine Avatar answered Sep 23 '22 10:09

Fouad Boukredine


I perform a quick test. If you declare function in the constructor, two object instances have different function instances even after optimizations. However with prototype, you have only one instance of the function which explains the performance difference.

    var Person = function () {
        var self = this;
        self.firstName = null;
        self.lastName = null;
        self.fullName = function () {
            return self.firstName + self.lastName;
        };
    };

    Person.prototype.fullName2 = function () {
        return this.firstName + this.lastName;
    };

    var a = new Person();
    var b = new Person();

    console.log(a.fullName == b.fullName); // returns false
    console.log(a.fullName2 == b.fullName2); // returns true
like image 27
Ersin Basaran Avatar answered Sep 23 '22 10:09

Ersin Basaran