I'm having a bit of trouble trying to get class variables to work in javascript.
I thought that I understood the prototype inheritance model, but obviously not. I assumed that since prototypes will be shared between objects then so will their variables.
This is why this bit of code confuses me.
What is the correct way to implement class variables?
function classA() {};
classA.prototype.shared = 0;
a = new classA;
//print both values to make sure that they are the same
classA.prototype.shared;
a.shared;
//increment class variable
classA.prototype.shared++;
//Verify that they are each 1 (Works)
classA.prototype.shared;
a.shared;
//now increment the other reference
a.shared++;
//Verify that they are each 2 (Doesn't Work)
classA.prototype.shared;
a.shared;
UPDATE: So it seems that everyone is confirming the fact that by incrementing the instance's variable we don't affect the prototype. This is fine, this is what I have documented in my example, but doesn't this seem like an error in the design of the language? Why would this behavior be desirable? I find it weird that when the instance's var is undefined we follow the hidden link to the prototype where we get the value of the var, but we copy it into the instance object.
I also understand that this isn't java/c++/ruby/python, it's a different language. I'm just curious as to why this behavior might be good.
If you instantiate that class (
a = new classA
), then modifying that instancea
won't change the base class itself. Instances ofclassA
will inherit everything fromclassA.prototype
, but that doesn't apply backwards, changinga
won't changeclassA
.If you have two instances like
a1 = new classA
anda2 = new classA
then you can make changes to botha1
anda2
without effecting the other. ChangingclassA.prototype
on the other hand will be visible in both of them.The variable
shared
of instancea
will have the default value until it is given a new value. The default value is the value ofclassA.prototype.shared
.It's because prototypes are not class definitions. Prototypical variables are not static variables. Think of the word prototype. It's not a model used to build an object -- it is an example object to be duplicated.
They are, but this:
is not doing what you think it's doing. It's in fact (approximately) sugar syntax for:
(the -1 being to return the pre-increment value, not that you're actually using the retrun value, but still.)
So this is actually doing an assigment to a.shared. When you assign to an instance member you are always writing to that instance's own members, not touching any members of any of its prototypes. It's the same as saying:
So your new a.shared hides the prototype.shared without altering it. Other instances of classA would continue to show the prototype's value 1. If you deleted a.shared you would once again be able to see the prototype's variable that was hidden behind it.
Incrementing the
shared
property via the instance makes it a property of that instance, which is why you're seeing this behaviour.Once you've done that, you'll never be accessing the prototype for that property through the instance, but its own property.
This is why the correct solution is to use
ConstructorA.shared
, as suggested in many of the answers so far, and always access it through the constructor function, not an instance.It might help to consider that there's no such thing as a class in JavaScript. "Instances" created with the
new
operator are just objects which have been created by a particular constructor function and have a particular prototype chain. This is whya.shared
won't be able to accessConstructorA.shared
- property access involves looking at the object in question for the named property, and failing that, walking its prototype chain looking for the property, but the constructor function which created the object isn't part of the prototype chain.If you want to have a class variable, something like a static variable in Java, then you can declare a variable in the parent class, but then you shouldn't access it as a variable of the child objects. This article has a nice example of the class Circle having the variable
Circle.PI = 3.14
while all the instances of Circle access it asCircle.PI
(instead ofc.PI
).So my answer is that if you want to have a class variable
shared
inclassA
then you shall declare the variable shared inclassA
, and later you should useclassA.shared
instead ofa.shared
. Changinga.shared
will never result inclassA.shared
being changed.You just put the member right on the "class" which in JavaScript is the function that constructs objects: