BenM
BenM

Reputation: 4278

Why should you not add functionality to a JavaScript constructor but instead via prototype?

Im look at Addy Osmani's chapter on the constructor pattern: http://addyosmani.com/resources/essentialjsdesignpatterns/book/#constructorpatternjavascript and I came across the following:

function Car( model, year, miles ) {

  this.model = model;
  this.year = year;
  this.miles = miles;

  this.toString = function () {
    return this.model + " has done " + this.miles + " miles";
  };
}

// Usage:

// We can create new instances of the car
var civic = new Car( "Honda Civic", 2009, 20000 );
var mondeo = new Car( "Ford Mondeo", 2010, 5000 );

// and then open our browser console to view the 
// output of the toString() method being called on 
// these objects
console.log( civic.toString() );
console.log( mondeo.toString() );

He said this was not a great thing to do with regards to the this.toString function as it isn't very optimal and isn't shared between all instances of the car type. But he doesn't explain what exactly this means and why it's a bad thing. He recommends to do the following:

function Car( model, year, miles ) {

  this.model = model;
  this.year = year;
  this.miles = miles;

}


// Note here that we are using Object.prototype.newMethod rather than 
// Object.prototype so as to avoid redefining the prototype object
Car.prototype.toString = function () {
  return this.model + " has done " + this.miles + " miles";
};

// Usage:

var civic = new Car( "Honda Civic", 2009, 20000 );
var mondeo = new Car( "Ford Mondeo", 2010, 5000 );

console.log( civic.toString() );
console.log( mondeo.toString() );

Can someone explain why using the prototype object to add in this functionality is optimal/better?

Upvotes: 5

Views: 154

Answers (5)

Pointy
Pointy

Reputation: 413712

Functions (well, more generally, properties) on the prototype object are shared by all instances. That single "toString" function will remain just one solitary object no matter how many "Car" instances are made. When you do the assignment in the constructor, a new function object is created for each one.

Obviously, if each instance needs a property that may vary from those of other instances, then you need a per-instance property.

Upvotes: 6

jcreamer898
jcreamer898

Reputation: 8189

A typical rule of thumb for me is to set properties that are specific to a given instance such as name, color, etc in the constructor and define functions that a "class" should share as a function on the prototype.

There are also performance ramifications of defining functions on the prototype vs on the constructor. When you define a function on the constructor, each time you use the constructor you are redefining the function again and again...

http://jsperf.com/prototype-vs-instance-functions

You wouldn't want something like name on the prototype because if you went in and changed Car.prototype.name, then you'd change the name for all new instances of Car.

Hope that helps!

Upvotes: 2

Lepidosteus
Lepidosteus

Reputation: 12027

Functions on the prototype are shared between every instance. This means that this is the exact same function for every instances, not an identical copy.

If you define the function in the constructor instead, each instance has to create its own version of the function, and then own it for itself. That means if you have 100 instances, you have 100 copies of the function in memory. It also means that updating the function is not fun at all, you would have to do it on every instance.

If the function is in the prototype, updating it in the prototype means every instance now use the updated code (because they all use the same, shared, function)

For example:

function Car( model, year, miles ) {

  this.model = model;
  this.year = year;
  this.miles = miles;

}

Car.prototype.toString = function () {
  return this.model + " has done " + this.miles + " miles";
};


var civic = new Car( "Honda Civic", 2009, 20000 );

console.log( civic.toString() );

Car.prototype.toString = function () {
  return this.model + " has done " + (this.miles * 1.60934) + " kilometers";
};


console.log( civic.toString() );

Note how I did not need to change the civic instance, it uses the new code and display the distance in kilometers.

Upvotes: 2

Richard Dalton
Richard Dalton

Reputation: 35793

When the function is inside the constructor it will be created for every new instance of the 'class'. So each Car object you create will have it's own toString function taking up extra memory.

If you add the toString via the prototype, it will only exist once and each of the Car objects you create will use this same function.

It also tidies up the constructor code to only include what is relevant to creating the object.

Upvotes: 2

AbstractProblemFactory
AbstractProblemFactory

Reputation: 9811

Everytime u run new Car JS parser executes code in constructor, which mean u are creating another

this.toString = function () {
    return this.model + " has done " + this.miles + " miles";
  };

With prototype approach there is always only one copy of toString().

Upvotes: 1

Related Questions