moon prism power
moon prism power

Reputation: 2347

Why declare an instance property in prototype instead of constructor?

I completely understand why it's better to use the prototype instead of the constructor to define a class method, (i.e. Use of 'prototype' vs. 'this' in JavaScript?) However, I recently came across a HashMap class that defines the count property in the prototype and the map property in the constructor:

js_cols.HashMap = function(opt_map, var_args) {

    /**
     * Underlying JS object used to implement the map.
     * @type {!Object}
     * @private
     */
    this.map_ = {};

    /...
}

/**
 * The number of key value pairs in the map.
 * @private
 * @type {number}
 */
js_cols.HashMap.prototype.count_ = 0;

Are there advantages of declaring an instance property like count in the prototype instead of saying this.count_ = 0; in the constructor? And if so, why not also js_cols.HashMap.prototype.map_ = {};?

Edit: A similar question was asked, Why declare properties on the prototype for instance variables in JavaScript, and "default values" was raised as a use case, but it was not explained why this is more desirable than just defining the default value in the constructor.

Upvotes: 6

Views: 647

Answers (5)

moon prism power
moon prism power

Reputation: 2347

I don't think the HashMap class is the best example of the benefits of declaring an instance property in the prototype. Consider instead a Response class:

function Response() {
   this.headers = {};
}
Response.prototype.statusCode = 200;

All instances of Response will share the statusCode of 200 — literally, the same number in memory will be shared across all instances. This makes sense if you expect your server to respond with status code 200 most of the time. Those Requests that need different status codes can overwrite their default statusCode, which creates a new number in memory.

I.e. If you have 10,000 concurrent requests, and 5 of them are 404's, you got away with only having 6 numbers in memory to represent all 10,000 statusCodes.

In summation, it might be worth it to use prototype if you expect a lot of instances to share the same default value most of the time. Otherwise, you can chalk it up to coding style. (Obviously for static properties, always use prototype.)

Note see Lucas's answer for why a property that's an object (e.g. map or headers) can't be in the prototype: all instances would be sharing the same reference, thus any update to that reference would update all instances.

Upvotes: 5

Ajinkya
Ajinkya

Reputation: 846

Consider class Customer representing a customer of a bank whose interest rates are a subject to change. For each customer you have to calculate interest, which was set per the the policy of the bank. In this scenario there is a variable interest whose value is same for certain customers, also the change to this variable must be propagated to all the customers. Let's model it in JavaScript.

var Customer = function(name, amount) {
    this.name = name;
    this.amount = amount;
};

Customer.prototype.interest = 10; //default value. which changes later
Customer.prototype.computeInterest = function() {
    this.amount += this.amount * this.interest / 100; //assume they are savings account
};

var me = new Customer('Ajinkya', 100);
var you = new Customer('RKBWS47', 1000);
var somebody = new Customer('Jon Doe', 10000);

me.computeInterest();
you.computeInterest();
somebody.computeInterest();

console.log(me.amount); //110
console.log(you.amount); //1100
console.log(somebody.amount); //11000

Customer.prototype.interest = 20; //yay!

me.computeInterest();
you.computeInterest();
somebody.computeInterest();

console.log(me.amount); //132
console.log(you.amount); //1320
console.log(somebody.amount); //13200

This is true for the methods too. They are shared by all the instances of the class with the this variable set to the invoking object or instance. You can also use the properties defined on constructor functions to simulate static methods or the way I like to remember them, methods called on classes. Using this.interest in our example wouldn't have allowed us to share it across me, you and somebody.

EDIT: I have answered assuming that the HashMap example is used to illustrate your question.

Upvotes: 0

Lucas Trzesniewski
Lucas Trzesniewski

Reputation: 51330

It works for count_, but it wouldn't work for map_.

count_ is an integer value, while map_ is an object reference. If a HashMap instance updates count_, it will essentially create a count_ property on its own instance.

But, if it inserts something to the map_, as all instances share the same map_ reference, all instances would end up with the same values in the map. This wouldn't work, and that's why every instance get a different map_ reference in its constructor.

As for the count_ property, it's declared in the prototype just as a default. It would be the same if it was instantiated in the constructor. I guess it's just a matter of coding style.

Upvotes: 1

Axel Richter
Axel Richter

Reputation: 61860

prototyping is the JavaScript OOP way to extend and inherit. So it is the way to make code reusable. You may be the programmer of the constructor, but others may wish to extend your Objects, if they reuse your code. And you may not wish, that they hacking around in your code.

Good description is here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Details_of_the_Object_Model

Greetings

Axel

Upvotes: 0

Lochemage
Lochemage

Reputation: 3974

It depends on how it is used, are you sure count_ doesn't define the number of hash buckets there are, rather than the exact count of how many items are in the hash? Defining variables in the prototype is good if you expect the value to remain constant and global for all instances of the class, since only memory for one variable is being used regardless of how many class instances you make.

Upvotes: 1

Related Questions