FANDOM


At first glance, objects in JavaScript don't seem to offer much in the way of access levels (i.e., "public" and "private" member variables and methods). However, there are aspects of the design of the JavaScript language that actually do allow for such results to be achieved, although some of them might be considered loopholes.

For a basic rundown of the options available, please refer to the External links section below.


CaveatsEdit

Private access to thisEdit

Private methods do not generally get access to this. The workaround for this is to use a private "self" variable, as demonstrated in Douglas Crockford's examples. I.e.,

function MyObject()
{
  // PRIVATE MEMBER VARIABLES
  var self = this;
  var myRealAge = 30;
 
 
  this.numWrinkles = 0;
 
  var grow = function()
  {
    myRealAge++;
    self.numWrinkles += Math.max(myRealAge - 30, 0);
  }
 
  this.getOlder = function()
  {
    grow();
  }
}

Here, if I had used this.numWrinkles instead of self.numWrinkles, numWrinkles would have been unchanged. Try it yourself.

Watch and private variablesEdit

Priveleged functions don't always have access to private variables - specifically, I've found that when privileged functions are called as part of watches. Here's some sample code to show what I mean:

function HumanBody()
{
  // PRIVATE MEMBER VARIABLES
  var myRealAge = 30;
  
  // PUBLIC MEMBER VARIABLES
  this.myApparentAge = myRealAge;
  
  // PRIVILEGED METHODS
  this.setApparentAge = function(prop, oldval, newval)
  {
    return Math.max(myRealAge * 0.8, newval);
  }
  
  this.watch("myApparentAge", this.setApparentAge);
}

HumanBody is a class that has one public property - myApparentAge. myApparentAge has a watch set on it that should theoretically not allow a HumanBody to set their apparentAge to be less than 80% of their realAge. However, this doesn't actually work. What actually happens is that whenever myApparentAge is attempted to be edited by the outsidie world, it gets set to "NaN." What the heck?

I think what's happening is that setApparentAge is being called from the wrong context - most likely, the context of Object, in which myRealAge is undefined. I've tried making setApparentAge a private method, and it still fails in the same way.

A workaround I've discovered is to use "proxy" methods. The one with the least amount of changes looks like this:

function HumanBody()
{
  // PRIVATE MEMBER VARIABLES
  var myRealAge = 30;
  
  // PUBLIC MEMBER VARIABLES
  this.myApparentAge = myRealAge;
  
  // PRIVILEGED METHODS
  this.setApparentAge = function(prop, oldval, newval)
  {
    return Math.max(myRealAge * 0.8, newval);
  }
  
  // PROXY METHODS
  var setApparentAgeProxy = function(prop, oldval, newval)
  {
    return this.setApparentAge(prop, oldval, newval);
  }
  
  this.watch("myApparentAge", setApparentAgeProxy);
}

What I've done above is created a private "proxy" method. I chose to make it private simply to keep /dumps of the object less cluttered. The proxy method is able to call the privileged method, because in the context it's called, this is properly set to the object (Note that in normal calls to private methods, this is not properly set! This is a special case, probably for the same reason that private variables aren't accessible in this context).

So this method works. However, it'd probably be even more elegant to hide all the logic used inside the setter method as a private method. This complicates matters a bit further, because, as we've already discovered, we can't directly call a method from a watch() call. Here's a way in which I've managed to hide the internals:


function HumanBody()
{
  // PRIVATE MEMBER VARIABLES
  var myRealAge = 30;
  
  // PUBLIC MEMBER VARIABLES
  this.myApparentAge = myRealAge;
  
  // PRIVILEGED METHODS
  this.setApparentAge = function(prop, oldval, newval)
  {
    return setApparentAge(prop, oldval, newval);
  }
  
  // PRIVATE METHODS
  var setApparentAge = function(prop, oldval, newval)
  {
    return Math.max(myRealAge * 0.8, newval);
  }
  
  // PROXY METHODS
  var setApparentAgeProxy = function(prop, oldval, newval)
  {
    return this.setApparentAge(prop, oldval, newval);
  }
  
  this.watch("myApparentAge", setApparentAgeProxy);
}

The solution isn't that elegant from the original coder's point-of-view, but from an API point-of-view, it's great. I've got a private proxy to a privileged proxy to the real private method. Note that setApparentAge and this.setApparentAge are two different functions. Now when I /dump the object, I'll see one setApparentAge function - with its only contents being "setApparentAge(prop, oldval, newval)".


External LinksEdit

Ad blocker interference detected!


Wikia is a free-to-use site that makes money from advertising. We have a modified experience for viewers using ad blockers

Wikia is not accessible if you’ve made further modifications. Remove the custom ad blocker rule(s) and the page will load as expected.