Private static class vars in javascript
Here's an addition to the recently named Module Pattern. Let's add private static class vars.
Let's assume we want to create an object class that will be used via the new operator, but there's a need to have a shared but private data space between instances. Here's how:
Example code
// always contain your code in a namespace
var LSN = { example : {} };
LSN.example.Foo = function () {
// vars and methods declared in this scope will be shared by instances
var static_bar = 0;
// private static functions
function incrementBar() { ++static_bar; }
function getBar() { return static_bar; }
// Create a function that will be returned as the Foo class constructor
// referencing static vars and methods along with instance
function InstanceConstructor(instance_seed) {
// instance private variable
var instance_bar = instance_seed;
// instance private method (until exposed in the returned object)
function incrementInstanceBar() { --instance_bar }
// instance private method affecting the class static var
function splitStaticBar() { static_bar = Math.floor( static_bar / 2 ); }
return {
incrementStaticBar : incrementBar, // method affecting static class vars
decrementStaticBar : function () { --static_bar }, // another, less efficient way
splitStaticBar : splitStaticBar, // slightly less inefficient way, but still not preferable
getStaticBar : getBar, // get the shared static value
incrementInstanceBar : incrementInstanceBar, // method affecting instance private var
decrementInstanceBar : function () { --instance_bar }, // another, less efficient way
getInstanceBar : function () { return instance_bar } // get the instance private var
}
}
// return the function that will be used to construct the instances
return InstanceConstructor;
}();
// Proof is in the pudding. Create two instances of Foo
var t1 = new LSN.example.Foo(10);
var t2 = new LSN.example.Foo(2);
t1.getStaticBar(); // returns 0
t2.getStaticBar(); // returns 0
// Have one instance modify the private class var
t1.incrementStaticBar();
t1.getStaticBar(); // returns 1
t2.getStaticBar(); // also returns 1
for (var i = 0; i < 5; ++i) {
t2.incrementStaticBar();
}
// Modify the static var with a method declared in the instance function scope
t1.splitStaticBar();
t1.getStaticBar(); // returns 3
t2.getStaticBar(); // also returns 3
// Play with instance variables as well
t1.getInstanceBar(); // returns 10
t2.getInstanceBar(); // returns 2
t1.incrementInstanceBar();
t1.getInstanceBar(); // returns 11
t2.getInstanceBar(); // returns 2
Points of interest
It's a pretty simple change from the basic instance creator function. Just wrap the instance creator function in another function—what I'm calling a class creator function. All vars declared in the class creator function scope are shared across all instances created with the returned instance creator function.
The methods affecting the class vars can be declared in the instance creator function scope, but I think it's cleaner to have the class var accessor methods in the class creator scope unless they're also modifying instance variables.
In my example, I chose to use prototypeless instance creation, returning a simple object with function refs to modify private class and instance data. It is possible to use a prototyped instance creator function and assign methods to the prototype that will affect the private class vars (but not the private instance vars). For example
...
function InstanceConstructor(instance_seed) {
// instance private variable
var instance_bar = instance_seed;
// instance private method (until exposed in the returned object)
function incrementInstanceBar() { --instance_bar }
// instance private method affecting the class static var (dead code. see NOTE below)
function splitStaticBar() { static_bar = Math.floor( static_bar / 2 ); }
this.incrementInstanceBar = incrementInstanceBar;
this.decrementInstanceBar = function () { --instance_bar };
this.getInstanceBar = function { return instance_bar }
}
InstanceConstructor.prototype.incrementStaticBar = incrementBar;
InstanceConstructor.prototype.decrementStaticBar = function () { --static_bar };
InstanceConstructor.prototype.getStaticBar = getBar;
// NOTE: we can't reference the splitStaticBar function, since it was declared in the instance creator function scope, but we're back in class creator scope
return InstanceConstructor;
Practical uses?
This would be useful when used with prototypeless instance construction where you want to create only one instance for a given set of construction params, and redistribute the cached instances on future calls to new. At the moment, I'm tired and that's all I can come up with. I'll let smarter people devise clever uses for it.
Update
Richard Cornford sent me an excellent and far more thorough write up on the idea. Good reading. I found the use of the much derided with particularly intriguing.