Javascript Visibility

I often find myself reinventing the wheel and get lost around closures, prototypes, unscoped variables, and other OOP-like features of Javascript. So I created a Javascript ‘class cheat-sheet‘ with the several ways of instantiating and referencing objects therein. I’ve eliminated redundant styles and what I found useless (unscoped globals). Without further ado:

function Class() {
  //
  // PUBLIC INSTANCE
  // instance public variable and methods
  //
  this.instance_public = 2;

  this.instance_public_func = function() {
    return private_var +
      this.instance_public +
      this.prototype_public;
  }

  //
  // PRIVATE
  // private variable and methods
  //
  var  that = this;
  var  private_var = 1;

  function private_func() {
    return private_var +
      that.instance_public +
      that.prototype_public;
  }

}
//
// PROTOTYPE PUBLIC
// prototype public variable and methods
//
Class.prototype.prototype_public = 4;

Class.prototype.prototype_public_func = function() {
    // private_var (hidden from prototype)
    return this.instance_public +
      this.prototype_public;
}

What does it mean? Well, I am aware of three ways to declare a field (basic variable) within a constructor (class definition) and one in the prototype. These are:

function Class() {
  var private;
  this.public1;
  global;
}
Class.prototype.public2;

The var scopes a object within its surrounding { } block. Such objects are accessible (through closure) to any function within the same block, but invisible outside of the block.

In the example above, public1 and public2 have the same visibility. However, the latter is memory efficient. public2 acts somewhat like a static class variable except that it can be overridden by any instance. Explicitly referencing prototype.public2 is akin to a static member in other languages such as Java. The . (dot) before the variable name that binds it to an object and makes it public, whether that object is this, that or var hash = {}; hash.key = "value";.

Any unbound object (via dot (.) or var) is global. There are very few (perhaps no) good reasons to do this.

Caveats

Note that function x is shorthand for var x = function. The following two methods are equivalent.

  function private_method1() {
    return that;
  };

  var private_method2 = function() {
    return that;
  };

Private and global functions defined within a constructor do not have access to the implied new object reference this, but can access another reference such as var that = this;. Perhaps this is a ‘bug’ in the ECMA standard.

Private (var) and instance public (this.) members, may access all other members of a class, including prototype variables and functions. However, prototype members may not access private members defined within the constructor. The following will fail:

   function X() { var priv = 6; };
   X.prototype.proto = function() { return priv; };
   var x = new X();
   alert(x.proto());

Private and prototype functions may be called within the constructor before they have been defined. But instance public are only available (at runtime) after they’ve been instantiated. For example, “priv and proto” will be alerted, but an error will be thrown on line 3 because this.pub has not yet been defined.

 1    function X() {
 2      alert(priv() +" and "+ this.proto());
 3      alert(this.pub());
 4      function priv() { return "priv"; }
 5      this.pub = function() { return "pub"; }
 6    }
 7    X.prototype.proto = function() { return "proto"; };
 8    new X();

Variations

A singleton object has only one instance and there are thus no advantages of the prototype.

var s = new function S() {
  this.pub_var = 1;
  this.pub = function() {};
  var that = this;
  function priv() {};
}();

Slightly more exotic, is a singleton whose members are all public:

var h = {
  field : "public",
  method : function() { return this.field; }
};

Returning the singleton from a constructor, gives us access to private variables. Consider the “Module Pattern”:

var Namespace = {
  C : function() {
    var priv = "priv";
    return {
      field : "pub",
      method : function() { return priv +" and "+ this.field; }
    }
  }
};
var c = new Namespace.C();