True private variables in JavaScript with prototype

This post was originally published on my old back on September 26th, 2011. The content provided here is a republishing of the original post.

Most folks know how to mimic public, private, and “privileged” variables in Javascript, but I think at some point, when getting familiar with prototypal inheritance, most scripters wonder if there is a way to access the private members (the ones created in a closure) of an instance within a function defined using prototype.

There is really no good way to do this. Here’s a simple prototype example:

function Cat(name, age)
{
   this.name = name;
   this.age = age;
}
Cat.prototype.introduce = function()
{
   alert(‘I am a cat. My name is ‘ + this.name +, and I am ‘ + this.age + ‘ year(s) old.’;
};
 
var c = new Cat(‘Garfield’, 7);
c.introduce();

The introduce function was defined with .prototype so the this keyword in that context refers to the Cat instance in question. Using it, we can get at the .name and .age properties. But this also means that the introduce function isn’t the only thing that has access to those properties. In fact, the whole world has access. They are, essentially, public properties.

alert(c.name);    // directly accessible
alert(c.age);     // directly accessible

What if you don’t want that? What if you want to take the name that is passed into the constructor, modify it in some way (let’s say, convert it to uppercase) and then offer read only access to it?

function Cat(name, age)
{
   this.name = name.toUpperCase();
}
Cat.prototype.getName = function()
{
   return this.name;
};
 
var c = new Cat('Garfield', 7);
alert(c.getName());     // GARFIELD
c.name = 'STEPHEN';     // oops, should this be allowed?
alert(c.getName());     // STEPHEN, clearly not the kind of encapsulation we're going for

So how can we allow getName to have access to .name, but at the same time disallow access to .name outside of prototype defined functions? Well, as I said, you really can’t. I mean, there’s no good way. But there is a sort-of way.

DISCLAIMER: I am NOT recommending that you actually do what I’m about to demonstrate. At the same time, I am not telling you NOT to do it either. From a performance perspective, this is completely unvalidated (at least by me). So if you want to do something like this, best do your homework and figure out if it will offer you an acceptable level of performance — I am not attempting to answer that for you.

Alright. With the disclaimer out of the way, here we go:

var Cat = function()
{
   var guid = 0;
   var privates = {};
 
   function cat(name, age)
   {
      this._$guid = guid++;
      privates[this._$guid] =
      {
         name: name,
         age: age
      };
   }
   cat.prototype.getName = function()
   {
      return privates[this._$guid].name;
   };
   cat.prototype.getAge = function()
   {
      return privates[this._$guid].age;
   };
   return cat;
}();
var cat1 = new Cat('Garfield', 7);
var cat2 = new Cat('Fluffy', 13);
alert(cat1.getName === cat2.getName);     // true
alert(cat1.name);          // oops, no dice (which is good)
alert(cat1.getName());           // Garfield
alert(cat2.age);           // oops, no dice
alert(cat2.getAge());            // 13

First, I alert cat1.getName === cat2.getName to illustrate that prototype really is being used here. The instances cat1 and cat2 share the implementation of the getName function (they don’t each get their own — something that would have been true had we used the closure model exclusively).

Second, you’ll notice that I do not have direct access to .name or .age. So this seems to do what we want.

Now, more alert readers will no doubt have noticed that I cheated. I’m still using the this keyword from within my prototype defined functions, getName and getAge. This is so that I can access the instances GUID (._$guid). The GUID is, of course, nothing more than counter, but it serves as a way to track the created instances. Using it, we can get access to property bag in the privates hash that “belongs to” the particular Cat instance we’re interested in.

But there is a flaw here, of course. The ._$guid, despite its marginally cryptic name, is completely unprotected. That is, the whole world has access to it. So one could really hork things badly if he decided to do something like:

var garfield = new Cat('Garfield', 7);
var fluffy = new Cat('Fluffy', 10);
 
fluffy._$guid = 0;
alert(fluffy.getName());      // Garfield, oops!  this is NOT good

Now, in this example, I chose a valid GUID (that of Garfield), but I could have just as easily chosen an invalid GUID, which would have caused a script error. Of course, the script error is easily preventable by ensuring that the ._$guid actually exists as a key in the privates hash before attempting to access it, but this does nothing to ensure the integrity of the data, and that’s a problem.

Turns out though, that we can add a bit more code. A bit of code that involves a check prior to accessing data from the privates hash to ensure that the instance’s _$guid hasn’t been tampered with. If it has, we throw an exception and refuse to go any further.

Here’s the revision:

var Cat = function()
{
   var guid = 0;
   var privates = {};
   var check = {};
 
   function cat(name, age)
   {
      this._$guid = guid++;
      check[this._$guid] = this;
      privates[this._$guid] =
      {
         name: name,
         age: age
      };
 
   }
   cat.prototype.getName = function()
   {
      if (check[this._$guid] !== this)
      {
         throw 'Oops, looks like this instances has been tampered with.';
      }
      return privates[this._$guid].name;
   };
   cat.prototype.getAge = function()
   {
      if (check[this._$guid] !== this)
      {
         throw 'Oops, looks like this instances has been tampered with.';
      }
      return privates[this._$guid].age;
   };
   return cat;
}();
var fluffy = new Cat('Fluffy', 7);
var snowball = new Cat('Snowball', 12);
var garfield = new Cat('Garfield', 9);
alert(fluffy.getName());      // Fluffy
alert(snowball.getName());    // Snowball
alert(garfield.getName());    // Garfield
fluffy._$guid = 2;
alert(fluffy.getName());      // Throws an exception, b/c fluffy's _$guid was modified

I even created a nice illustration to help you visualize this :-)

Visualization of private variables using prototype in Javascript

As you can plainly see, the keys in the privates hash correspond to the ._$guid property of Cat instance. In other words, if you match up an instance’s ._$guid with a key in the privates hash, you’ll have access to that instance’s collection of member variables.

You’ll also notice that the keys in the check hash match the keys in the privates hash. However, they don’t point to a property bag, they point to an actual Cat instance. In the getName function, we have access to the instance (through the use of the this keyword), and we can use that to perform a check. If an instance’s public ._$guid has been tampered with, then looking in the check hash for that $_guid will result in accessing a Cat instance that isn’t equal (identical) to this. In that case, we can throw an exception. Otherwise, we go ahead and access and entry from the privates hash, and this gives us access to the current Cat’s members.

Before closing, I want to once again make clear that I am most definitely NOT recommending that you use this model, but neither am I discouraging the use of it (I’m a little bit annoying that way, I know). It’s simply untested in terms of memory and speed performance. Whether or not this yields anything that would constitute acceptable performance, I do not know and have not attempted to determine. I imagine there could be some scenarios in which it is probably okay. And I imagine there are other scenarios in which it might be a nightmare, but I’m only guessing on both counts.

If you decide to experiment with this, definitely report back here with your findings for all to see.

Happy scripting!