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.