Private static class vars in javascript

By Luke Smith on June 19, 2007 9:40 PM

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.

2 Comments

  1. Gravatar

    Very interesting. This is a pattern I haven't seen used before. I'm not sure this exact pattern will come in handy very often, but I wonder if a similar technique could be used for implementing more traditional inheritance of private static members on top of the Module Pattern.

    I'll have to play around with this.

    Of course, every time I think I've invented a new JavaScript pattern I end up discovering that Douglas Crockford invented it first and I just didn't understand what he was trying to tell me because it was too damn brilliant for me to comprehend. What do you think are the odds he's already invented this one as well?

  2. Gravatar

    P.S. I'm never commenting on your blog again until you either provide some form of preview or make the comment layout (and typography) suck less. Ugh. You should be ashamed. :P

No TrackBacks (http://lucassmith.name/mt/mt-tb.cgi/89)

ls.n

LucasSmith.name

Luke and Heidi

I'm Luke. I am a front end engineer at Yahoo! on the YUI team.

Mostly I write about code stuff, but occassionally I'll mix in some real life. You've been warned.

Archives

Tags

Feeds

Subscribe to feed Recent entries

Content licensed under Creative Commons

Code licensed under BSD license

©2005 - 2010 Lucas Smith

Powered by Movable Type