True private variables in Javascript with prototype

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!

10 Responses

  1. Aseem Kishore Says:

    Very cool and neat idea! I love it.

    The only downside is that the private state is no longer automatically garbage collectable. So if you remove all references to an instance of an object, it will get garbage collected, but its private state won't.

    We ran into this problem with Seadragon Ajax, and we found no way around it, so we ended up going with the typical underscore naming convention. Real bummer, but all in all, not a huge deal.

    Hope all is well, Stephen!

  2. sstchur Says:

    Very good point. Thanks for contributing that bit Aseem. I suppose one might then caveat this approach further by saying that you'd have to follow a pattern whereby you have a .destroy method for your instances, and then you'd be stuck making sure you managed that appropriately. Definitely not desirable.

  3. Chris West Says:

    I actually wrote an article about creating truly private members in JavaScript which will allow the private members to be removed. Here is the page:
    http://gotochriswest.com/blog/2011/05/04/private-variables-in-javascript/

    I also just wrote a JavaScript implementation of an actual Hashtable class which uses this style of coding to achieve truly private variables:
    http://gotochriswest.com/blog/2012/11/06/javascript-hash-table-implementation/

  4. Igor Says:

    Hi sstchur,

    Great blog. I'm currently looking for a good default pattern when working with javascript objects, privacy and inheritance. Like in the pattern book by Stefanov, you show that even prototypes can be defined inside closures to allow access on private variables.
    Where i now stumble upon (and maybe also your solution) is the following problem: If you want to use inheritance and your Cat constructor were the supertype, then you would have a subtype like e.g. FeralCat. This would look like:

    var cat1 = new Cat('Garfield', 7);
    FeralCat.prototype = cat1;

    After the assignment, FeralCat.prototype cannot further be extended using the same smart way with closures. All of the subtype protoype members will have to be public.

    Maybe your blog does not claim to solve inheritance issues, maybe i miss a point. Anyway, what do you think?

  5. Mahn Says:

    This method redefines the prototype of the object with every instantiation, even though the prototype itself never changes, which makes it a bad idea in several fronts. Don't use unless you are absolutely positive you know what you are doing and the implications it has.

  6. sstchur Says:

    Igor, I honestly don't have much to offer you, unfortunately. I wasn't attempting to solve any sort of inheritance problems. In fact, I wasn't really attempting to solve any "real" problem with this post. It was more of an "information only" type post. I even say, directly in it, that I don't really recommend it. It was more a proof of concept — is it even possible to use prototype and still have truly private variables. That's what I was attempting to demonstrate.

    The truth though, is that I've never used this, and honestly don't think I ever would. I think it's kind of a bad idea in a real-world scenario in a number of way. But I DO think it was interesting to talk/blog about.

  7. Private Variables in Javascript | Dolan's Dissertation Says:

    [...] http://blog.stchur.com/2011/09/26/true-private-variables-in-javascript-with-prototype/ [...]

  8. tom Says:

    In the first example is a ) missing at the end before the semicolon
    : alert('I am a cat. My name is ' + this.name + ', and I am ' + this.age + ' year(s) old.';

  9. Vinay Says:

    This is the best possible way to attach private properties to objects unless nobody is touching the public 'guide' property. It'll work perfectly if we assume no one will change this private property.

    But we can use `Object.setProperty` thing to prevent this property from being overridden.

  10. Matt Says:

    I found this tool https://github.com/TremayneChrist/ProtectJS which allows you to create private methods on the prototype. It's pretty cool but doesn't support variables yet – I think that's coming!

Got something to say?

Please note: Constructive criticism is welcome. Rude or vulgar comments however, are not and will be removed during moderation.