Private Static Members in JavaScript

 
 
 

It was a widely held belief that JavaScript objects could not have private instance members, that all JavaScript object properties where public and could be accessed and changed with external code. Douglas Crockford has demonstrated that closures can be used to provide JavaScript objects with private members.

Douglas Crockford describes how the invocation of the constructor forms a closure that allows all of the parameters, local variables and any functions defined as function fncName(){ ... ); within the constructor remain associated with the object that is constructed as its private members. And how inner functions assigned to properties of the object instance (with this.methodName = function(){ ... };) become "privileged" methods. Privileged because they have direct access to the private members of the object. Apart from invoking the privileged instance members of an object, there is no way that external code can access the private members of the object instance. Public members are assigned to the constructor's prototype object in the traditional way (but public methods have no direct access to the instance's private members).

Public and private are terms directly from class based languages (joined by "package" and "protected" in Java), privileged is a term that I think Mr Crockford coined to describe the special role of the constructor's inner functions assigned to externally accessible instance members.

In Java the modifiers private, public, package and protected can be combined with additional modifiers, one of which is static. A member that is static is a member of the Class and not the instance. There is only one copy of the member per Class. In JavaScript static members are defined as properties of the constructor:

function MyObject(){
   // constructor function
}
MyObject.staticMember = "any value";

JavaScript static members defined as properties of the constructor object are public static members.

It was also widely believed that JavaScript static members must also always be public, even by people familiar with Douglas Crockford's technique for creating private instance members.

Private instance members are achieved by forming a closure when objects are instantiated. It is possible to form a closure that included the class constructor and local variables that would server as private class members by making a one-off inline function call that returned the constructor (there is only one constructor per class), as the page or JS file loaded.

var myObject = function(){
    /*private static (class) member*/
    var counter = 0;
    /*private static method.*/
    function incrementCounter(){
        return counter++;
    };
    /*class constructor.*/
    function constructor(id){
        this.id = id;
        var self = this;
        /*call private static (class)
        method and assign the returned
        index to a private instance member.*/
        var index = incrementCounter();
        /*privileged instance method.*/
        this.getIndex = function(){
            return index;
        };
    };
    /*privileged static (class) method
    (a property of the constructor)*/
    constructor.getNoOfInsts = function(){
        return counter;
    };
    /*return the constructor.*/
    return constructor;
}(); //simultaneously define and call (one-off)!

/*public static  member*/
myObject.pubStatic = "anything"

/*public instance member*/
myObject.prototype.pubInstVar = 8;

Having the one-off function call return the class constructor forms a closure that can hold private static members and assign privileged static methods (as properties of the returned constructor. This is essentially taking Douglas Crockford's method for creating private instance members and applying it to the constructor itself to produce the same effect at the class level.

In JavaScript inner functions have direct access to the parameters and local variables in their containing functions. In this case code within the constructor function can call the incrementCounter function as its name is a local variable name of the constructor's containing (outer) function.

There is an interesting side effect in that privileged instance methods become doubly privileged in that they also have direct access to the private static members of the class.

Public instance (defined as properties of the prototype) and static methods (properties of the constructor as returned by the inline function call) have no access to the private static members of the class.

Having taken the idea that made private instance members possible and applied it at a level that makes private static members possible the obvious next step is to apply the same method to groups of classes.

In class based languages a grouping of classes might be a package. I don't think that I would use package to describe groupings of classes in JavaScript. For one thing package members (private or otherwise) don't really exist, and for another, the recursive application of the principal allows for groupings of groupings of classes (and so on).

Remembering that each inner function has direct access to the parameters and local variables in its outer function(s) the result of grouping class constructors within a closure of their own, and possibly that closure within others, produces an unusual hierarchy of nested accessibility. An unexpected feature of JavaScript.

A hypothetical class grouping closure (anonymous in this case) might go like:-

var global = this;
(function(){
    var classGroupMember = 3;
    function privateToClassGroup(){
        /*this method cold be a utilitiy
        for the classes in the group or used
        as an internal object constructor.*/
        ...
    };
    global.MyObject1 = function(){
        var privteStaticMember = 4;
        function privateStaticMethod(){
			...
		}
        function constructor(id){
            ...
        };
        /*return the constructor.*/
        return constructor;
    }();  //simultaneously define and call!
    global.MyObject2 = function(){
        function constructor(id){
            ...
        };
        /*return the constructor.*/
        return constructor;
    }();  //simultaneously define and call!
    global.MyObject3 = function(){
        function constructor(id){
            ...
        };
        /*return the constructor.*/
        return constructor;
    }();  //simultaneously define and call!
})();  //simultaneously define and call!

The main drawback of this approach is that the constructor functions are created inline as the page loads. This means that any constructor returning function call cannot use a constructor or function that will be returned by a later function call. The order of loading must be considered so that constructor returning function calls happen after similar calls that they are dependent upon.

This is not the case for constructors defined with the standard function declarations as the creation of function objects from declarations precedes the execution of inline code.