Reputation: 2347
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
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
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
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
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
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