Information Hiding in JavaScript

Why would Bocoup, a company whose charge is to “move the open web forward,” be publishing an article on something so nefarious-sounding as “information hiding”? An article titled “Free Love & Information in JavaScript” would seem much more apt for this blog. Trust me: if information hiding were an inherently immoral practice, I wouldn’t know anything about it. I probably would have spent most of high school trying to convince my peers that I was actually way into information hiding (despite being afraid of it).

In terms of programming, “information hiding” refers to the practice of concealing implementation details that are unfit for the consumers of the code. (As per usual, Wikipedia offers a nice overview.) This prompts the question: what do we mean by “consumers”?

  • If you’re working on a library, the “consumers” would be the developers using your library in their application logic. In those cases, you would want to “hide” methods that aren’t part of the API but that you wrote for internal use (for example, you might want to DRY out your functionsby sharing code between them).
  • If you’re writing an application, “consumers” might refer to other developers on the project who use your module. As before, you wouldn’t want consumers to rely on details that you intended to change later on.
  • Since the browser is such an open execution environment, “consumers” could also refer to end users of the front-end application you are writing. Here, you might not want users to open up the JavaScript console and start tinkering with sensitive data and methods.

Whatever your particular perspective, I hope you can see how information hiding can be a useful practice when it comes to structuring code. Just like smoking, dancing, and attending the prom, it isn’t scary and it doesn’t have to end with you sobbing in your mother’s arms. If you’re with me on that, it’s time to take a look at how we might accomplish this in JavaScript.

Implementation Details

Like with most things JavaScript, web developers have a variety of options when it comes to hiding implementation details. Here, I’d like to talk about five distinct approaches: informal naming, per-instance closures, per-class closures, obscurity, and symbols. For each approach, I’ll include a code example for a simple Player class that implements:

  • private state for coins and lives
  • a private cashIn method
  • a public addCoin method

In all cases, the usage of the API will remain the same:

// Instantiate a player
var player = new Player();

// Award the player with two coins:
player.addCoin();
player.addCoin();

Informal Naming

A common convention among JavaScript developers is to simply prefix the names of “private” data and functions with an underscore (_) character. Many open- source JavaScript libraries follow this practice includingjQuery, Backbone.js,Ember.js, and Knockout.js. Because of this adoption, the practice serves as a reliable signal to consumers that, “hey, don’t use this–I might change it later”. Here’s how it’s done:

function Player() {
  this._lifeCount = 3;
  this._coinCount = 0;
}

// The underscore in front of `_cashIn` tells consumers that this method is not
// intended for public use.
Player.prototype._cashIn = function() {
  this._lifeCount += Math.floor(this._coinCount / 100);
  this._coinCount %= 100;
};

Player.prototype.addCoin = function() {
  this._coinCount++;
  if (this._coinCount > 99) {
    this._cashIn();
  }
};

Of course, simply prefixing a method name with an underscore doesn’t stop anyone from using it. This means that if you are interested in preventing consumers from monkeying around with your application or SDK (maybe cheating at a game or gaining unauthorized access), you’ll need to keep reading.

Per-instance closures

The “closure” in JavaScript can be a tricky beast, especially for developers just getting started with the language. Basically, a closure is created whenever a long-lived function holds a reference to (or “closes around”) a short-lived function. If we define implementation details within the scope of the constructor function, then public methods can “close” around them. This is starting to feel a bit academic, so let’s return to the running example:

function Player() {
  var lifeCount = 3;
  var coinCount = 0;

  // When defined this way, `cashIn` will not be available outside of the
  // constructor.
  function cashIn() {
    lifeCount += Math.floor(coinCount / 100);
    coinCount %= 100;
  }

  // We'll declare `addCoin` as an instance method by attaching it to `this`.
  this.addCoin = function() {
    coinCount++;
    if (coinCount > 99) {
      cashIn();
    }
  };
}

Here, you can see that cashIn cannot be accessed outside of the Playerconstructor. By closing over that function, however, the addCoin function can use it. We attach the addCoin method to the instance itself because, as a public API, we want this to be accessible to the consumer.

This approach suffers from two problems. The first relates to performance. Thanks to the concept of “prototypal inheritance”, instance methods in JavaScript are shared by all instances (classically-trained programmers may recognize this as the “flyweight pattern” described by the “Gang of Four”). This technique of information hiding eschews the performance benefits of code sharing–each instance defines a unique copy of the addCoin and cashInmethods.

Secondly, structuring code in this way does not scale particularly well. AnyPlayer method that needs to access the private API must be declared within the constructor. This requirement will encourage growth of the constructor function, making it more and more difficult to read and maintain.

Per-class closures

Instead of using the constructor as a closure for private methods, we could declare private methods statically and then close around the constructor and methods with anIIFE.

var Player = (function() {

function Player() {
  this.lifeCount = 2;
  this.coinCount = 0;
}

// The private `cashIn` function is not accessible outside the IIFE's scope
function cashIn() {
  this.lifeCount += Math.floor(this.coinCount / 100);
  this.coinCount %= 100;
}

Player.prototype.addCoin = function() {
  this.coinCount++;
  if (this.coinCount > 99) {
    // We use "call invocation" to make sure the context of the `cashIn`
    // function is set to this instance of `Player`
    cashIn.call(this);
  }
};

// We need to explicitly "export" the `Player` class so that it is available
// outside the scope of the IIFE
return Player;

})();

This approach successfully hides private methods, and those methods are shared by all Player instances. But slow down there, this approach isn’t perfect, either. You probably noticed that the instance variables lifeCountand coinCount are exposed for all the world to see. This “per-instance closure” approach only works for private methods.* So really, this approach is too niche to be generally useful.

Obscurity

Let’s take a harder look at the “informal” approach we first considered. That method was nice because it was memory-efficient and maintainable and because it supported both instance methods and instance data. If we could find a way to make those underscore-prefixed attributes truly private, we might have a real solution on our hands…

It turns out, we can! Sort of! Instead of hard-coding the private attributes with human-readable strings (i.e. "_addCoin" or "_lives"), we can name them with dynamic randomly-generated strings. Then, we can keep a lookup table to translate human-readable names to their randomly-generated counterparts (and hide that inside a closure).

Not sure what I’m talking about? Neither am I, at this point. Let’s return to the example for some clarity:

// We'll use an IIFE again so that our key isn't globally available
var Player = (function() {

// This is our map. Each time this code executes, the values of this object
// will be unique.
var KEY = {
  coinCount: Math.random(),
  lifeCount: Math.random(),
  cashIn: Math.random()
};

function Player() {
  this[KEY.lifeCount] = 3;
  this[KEY.coinCount] = 0;
}

Player.prototype.addCoin = function() {
  this[KEY.coinCount]++;
  if (this[KEY.coinCount] > 99) {
    this[KEY.cashIn]();
  }
};

Player.prototype[KEY.cashIn] = function() {
  this[KEY.lifeCount] += Math.floor(this[P.coinCount] / 100);
  this[KEY.coinCount] %= 100;
};

return Player;

})();

In this example, instead of using “dot notation” to dereference the Personinstance with a simple string (as in this.lifeCount), we’re using the KEYlookup table to retrieve the obscured name** (as in KEY.lifeCount), and using that name to dereference the instance (as in this[KEY.lifeCount). Notice how all this does not change the public API: person.addCoin() still works as intended.

This solution is perfect, isn’t it?! Actually, it’s a nightmare. First of all, who wants to write code like this? I don’t.

Second of all, we’re not so much hiding the information as we are obscuring it (you might say we’re hiding it in plain sight). If you were to inspect aPerson instance in your browser’s JavaScript Console, you would see that it defined two numeric attributes and one Function attribute (albeit with crazy names like 0.5115215787664056). While this makes it very difficult for a consumer to accidentally depend on these details (they change every time you refresh the page), any dedicated adversary could probe at them enough to reverse engineer our KEY lookup table. We could frustrate these efforts by making the private properties non-enumerable withObject.defineProperty, but that will only work in browsers that implement ECMAScript 5.

So while this will look great on our Wall of JavaScript Oddities, it probably has no place in production.

Symbols

For our last attempt to tackle this problem, we’ll be looking at functionality that doesn’t exist today. ECMAScript 6 (sometimes referred to as “ES6” or by its code name, “Harmony”) is the next version of the JavaScript language specification. It includes a lot of exciting new features, but for the purposes of this post, we’ll be focusing on Symbols. (If you’d like to learn more about ES6, you should watch our very own Rick Waldron‘s presentation,“ECMAScript 6: My Favorite Parts”.)

One word of warning: ES6 is not a finalized standard. Symbols are still being discussed, which means that the precise syntax discussed here may change over time. (It also means that you can participate in its definition–head on over to the es-discuss mailing listto get involved.)

That said, lets take a look at what Symbols are (conceptually), and why they might be useful in our goal to hide information. As you probably know, in today’s JavaScript, any value you specify as a key to an object is automatically coerced into a String. For example:

var myObject = {};
var objectKey = {};
// When we attempt to use an object as a key...
myObject[ objectKey ] = 4;

// It is coerced to a string, meaning the value is actually stored with the key
// '[object Object]' (the value returned by `Object.toString()`)
myObject['[object Object]'] === 4;

// This means that even though we might want to use a different object as a
// unique key for a different value...
myObject[ { a: 23 } ] = 6;

// ...the same attribute will be modified, since by default, all object share
// the same generic String representation.
myObject['[object Object]'] === 6;

Symbols are objects specifically designed to avoid this behavior: when used as keys to an object, they will not be coerced to Strings.

If we encapsulate the Symbols, then we can use them to define “private” attributes of publicly-accessible objects–true information hiding in JavaScript! Here’s how it might be done:

var Player = (function() {

// Define the Symbols that we'll use as keys for the private API
var lifeCount = Symbol(),
  coinCount = Symbol(),
  cashIn = Symbol();

function Player() {
  // When used to dereference the `Player` instance, Symbols will not be
  // converted to String values
  this[lifeCount] = 3;
  this[coinCount] = 0;
}

Player.prototype.addCoin = function() {
  this[coinCount]++;
  if (this[coinCount] > 99) {
    this[cashIn]();
  }
};

Player.prototype[cashIn] = function() {
  this[lifeCount] += Math.floor(this[coinCount] / 100);
  this[coinCount] %= 100;
};

return Player;

})();

This should look familiar–it’s basically identical to the “Obscurity” approach described previously (with Symbols replacing random numbers). Given the similarities, it’s reasonable to wonder if it’s actually an improvement at all. Because Symbols are unique objects in memory, they cannot be “forged” or “guessed” in the same way that string values can. We rejected the “Obscurity” approach because of this very weakness in String keys, so Symbols address the only flaw with that approach.

But wait! There’s more! The square brackets all over the above example can be kind of a drag to write. Lucky for us, an alternative syntax for working with Symbols in the context of ES6 modules makes them even more readable.

As I’ve already pointed out, ECMAScript 6 is still being specified. Different features have reached different levels of consensus. There’s no telling how “at-names” and the private keyword might change change as ES6 matures. What I’m about to show you is volatile–my brow was sweating and my hands were shaking as I painstakingly typed it all out:

var Player = (function() {

// Define private Symbols using the "at-name" syntax
private @lifeCount, @coinCount, @cashIn;

function Player() {
  // Use the Symbol with dot notation to dereference the Player instance!
  this.@lifeCount = 3;
  this.@coinCount = 0;
}

Player.prototype.addCoin = function() {
  this.@coinCount++;
  if (this.@coinCount > 99) {
    this.@cashIn();
  }
};

Player.prototype.@cashIn = function() {
  this.@lifeCount += Math.floor(this.@coinCount / 100);
  this.@coinCount %= 100;
};

return Player;

})();

This code just feels so much cleaner without all those square brackets, doesn’t it? Eagle-eyed readers will note that this code looks eerily similar to the code in the first “Informal Naming” approach. Really, the only differences are the initial declaration of symbols, and a replacement of the underscore character (_) with the “at” sign (@). I give a lot of credit to the language designers for this fact. This syntax recognizes the informal convention already in use today and “makes it real” through a trivial transformation.

I know I said that ECMAScript 6 doesn’t exist today, but we don’t have to let that stop us from playing with these ideas. Just like Dr. Emmit Brown, we can experiment with these visions of the future to create something remarkable.The Continuum project gives us a glimpse as to what it might be like to write JavaScript with next-generation features including, you guessed it, Symbols.

Conclusions

It’s been a long road, but we finally found a complete solution for information hiding in JavaScript… or did we? After all, ECMASript 6 still isn’t complete, let alone implemented in enough browsers for general use. Where does that leave us, the modern-day developers yearning for private state?

For my part, I’ll be sticking to the informal naming convention for the foreseeable future. No other approach is as recognizable, maintainable, or powerful as simply denoting private APIs with an underscore.

We can’t forget that convention alone will not stop malicious adversaries, but that problem is much bigger than information hiding. At the end of the day,the browser remains an inherently insecure execution environment; no amount of JavaScript trickery can fix that. If you are writing client-side code that needs to be trusted, my advice is to off-load sensitive operations to a secure server.

This may be a disappointing conclusion to our investigation, but sometimes simplicity trumps all other requirements.

* – In environments that implement WeakMaps from ECMAScript 6, you couldbuild a WeakMap that associated Player instances with private data, but as we’ll see, ES6 promises a far more convenient primitive for hiding information. ** – As avid readers of this blog know, Math.random() isn’t sufficiently random for cryptographic applications, but it should do for our purposes.

Comments

We moved off of Disqus for data privacy and consent concerns, and are currently searching for a new commenting tool.

  1. Oh man, I was really hoping to find a magical practical solution for actually creating private members in JS here. I’ve floundered about coming up with some similar impractical solutions or even inadvertently causing memory leaks. Underscores and waiting for ES6 it is 😉 Nice post either way.

    • My original thinking was that, \”If you have an ES6 environment, you should use the right tool for the job.\” You’re right, though: WeakMaps may actually be a viable option for some projects today. Thanks for adding an example!

  2. Great post – ES6 cannot come soon enough. In the meantime, it IS possible to have truly private per-instance variables and methods, without losing the benefits of prototypal inheritance or relying on obscurity.

    I’ve posted the code as a gist: https://gist.github.com/Ric

    Basically the trick is to create a globally unique identifier for each instance, and use that identifier as a key to set and retrieve secrets via a private variable.

    It won’t win a beauty contest but it works! (Unless someone is REALLY determined to hack it, that is…)

      • Good point! Thanks, I hadn’t thought of that.

        In any browser that supports Object.defineProperty (which is basically all of them except IE8 and below) it’s possible to make the GUID read-only. So there’s that. (I’ve updated my gist accordingly.)

        No doubt it’s still not foolproof, would welcome anyone telling me why it still doesn’t work!

        • You’re at the beginning of a long and twisting rabbit hole, my friend. 🙂

          Nathan Wall gave a great overview of this topic at jQCon PDX. His slides are here: http://nathan-wall.github.i

          Briefly, there are (at least) two serious problems remaining in your gist:

          1) malicious code can replace Math.random with an implementation that returns GUIDs of its choosing

          2) Object.defineProperty does not check if descriptor properties are own properties. So, malicious code can set `Object.prototype.configurable` and `writable` to true.

          • Ah, the joys of JavaScript. Think I’ll concede defeat for the time being! I would say that you could pass in `configurable` and `writable` options to guard against (2), but someone could easily overwrite Object.defineProperty…

            Will enjoy those slides. Thanks

  3. Nice writeup; well done.

    From a practical perspective I’d say that if there’s one thing I’ve learned working on the Dojo Toolkit (another open source project that uses the leading underscore \”private\” notation), it’s that if you’re writing code to be consumed by other developers, you’ll never anticipate all the ways people will want to use your code. Factoring functionality into tiny discrete bits of publicly-accessible code that people can override or augment on a granular basis makes your code FAR more useful to consumers than if you try to get too cute and Computer Science-y with your encapsulation (it can help keep your code testable too, though that’s more an issue of code architecture than anything directly related to information hiding itself).

    Hiding things in closures often feels right, but sometimes what people want is your code, but with a few lines changed; closure-based hiding can keep such people from making small neat monkey patches.

    Anyway, my point is only tangentially related to this post, but there’s a pragmatic aspect to this that I wish we talked about more often.

  4. While I am excited for whatever version of private scope ES6 brings to Javascript, the per-instance closure has always been my privatization choice. Though I’ve never seen performance impacts in my applications, I didn’t know they could be a culprit. Great article!

  5. Just a kind reminder:)
    Per-class closures
    var Player = (function() {

    function Player() {
    this.lifeCount = 2; // underscore is missing
    this.coinCount = 0; // underscore is missing
    }
    function Player() {
    this._lifeCount = 2;
    this._coinCount = 0;
    }

    • Thanks for the heads up! I’ve corrected the example, but I’ve taken a slightly different approach than what you’ve suggested. Instead of consistently prefixing the instance variables with an underscore, I’ve removed the underscore completely. Obviously this makes no functional difference, but it helps to differentiate the \”Per-class closures\” example from the \”Informal naming\” example.

  6. I like per-instance closures the best, as they seem to me to be the only way you can really hide internal state (other than through various forms of obscurity). As soon as you have any member variables associated with \”this\”, they are, one way or another, accessible on the object.

    You can mitigate the repetition of code somewhat in a per-instance closure by using a function declared outside the scope of the instance, called from within the instance.

    http://jsfiddle.net/Ysbbp/4/ is an example how I do this kind of thing. Though this has a different problem, as it moves the code for manipulating the data for a \”class\” away from its \”class\” definition.

    I’m not sure whether this improves the performance at all (you only have one small function declared per instance, rather than two larger functions). But, for me, it encapsulates the data better (I don’t like having properties hanging off an object unless I have to).

    If you need to access the members of the object in the private function for whatever reason, you could call the private function using apply(), passing the object as the first argument. Then the private function can refer to the object as \”this\” and get at its members.

    Any functions which don’t need the private state can still go on the prototype, of course.

    • Cool! I hadn’t thought of this approach. In your example, you are closing around what is essentially a secondary context object (`data`). You might even name it `privateThis` or something to make the similarity more apparent.

      As for the relative performance (compared to my per-instance closure approach), I have to imagine that this would be slightly less memory intensive (smaller functions, as you’ve pointed out), but more CPU intensive (since using these \”private\” methods requires two function calls).

      Of course, all this is probably a drop in the bucket when compared with your business logic, but it’s interesting to consider the performance ramifications anyway.

      • You could use the underscore approach (\”_data\”) as you suggested in your article, in tandem with the per-instance closure. That would make it clear enough.

        The performance hit might be minimal in terms of overall performance for a page, but substantial if you’re doing a lot of operations. I did a quick jsperf test to compare http://jsperf.com/per-insta… which seems to back this up.

          • Yes, good spot, you’re right. Your example makes the performance difference even more marked. There may also be some benefit in the original perf test, perhaps, as it includes performance differences produced by the different types of constructor. Though as you say, in a real app, that code would only run once.

  7. There has to be a balance between hiding data and maintainability. Debugging would become quite difficult if all the data is hidden. We might need to find a balance between both. How do you propose we debug these hidden values to fix issues?


  8. var Player = (function(){
    var method1 = function(){
    console.log('method1');
    };
    var PROP={};
    function Player(a, b){
    this._IDX = Math.random();
    PROP[this._IDX] = {
    a: a || 1,
    b: b || 2
    };
    }
    Player.prototype.set = function(a, b){
    PROP[this._IDX] = {
    a: a,
    b: b
    };
    };
    Player.prototype.method = function(){
    var prop = PROP[this._IDX];
    method1();
    console.log(prop.a, prop.b);
    };
    return Player;
    })();
    var p1 = new Player();
    p1.method();
    p1.set(3,4);
    p1.method();
  9. // very nice article that acts for me as a thinking basis !
    // until Harmony please consider this approach : a store in closure + getters on objects
    // it’s a kind of on-demand protection, it could work fine with a object instance
    // at prototype eother at constructor level…

    // of course you could SHA1’ing the keys of the Store for best effect
    // this works in a command-line nodejs, and ought to be tested in browser and benchmarked.

    /**
    * a function to add a protected Store
    * visible only in context of an object
    *
    * this emultates a kind of \"privacy\" for variables
    * (better saying \"protected\" values by a closure
    */

    // storify... or holdify !
    var storify = (function(scope, undefined) {

    // you could change these settings at your taste
    // storify.defaultAccessorNames = {} before using it
    var defaultAccessorNames = {
    hold : \"__hold\",
    recall : \"__recall\",
    show : \"__show\"
    };

    /**
    * @param obj
    */
    function storify(obj) {
    var an = defaultAccessorNames;

    // prevent property redefinition
    // (this would result in a TypeError)
    if(an.hold in obj) return;
    if(an.recall in obj) return;
    if(an.show in obj) return;

    // store, embarqu\u00e9 dans la closure de cette fonction
    var store = {};

    /*
    * store writing property
    *
    * note : if an exception occurs in a getter/setter, it fails silently
    */
    Object.defineProperty(obj, an.hold, {
    get : function () {
    return function mkhold(k, v) {
    if(this !== obj) throw new Error(\"bad context\");
    store[k] = v;
    }
    }
    });

    /*
    * store writing property
    *
    * note : if an exception occurs in a getter/setter, it fails silently
    */
    Object.defineProperty(obj, an.recall, {
    get : function () {
    return function mkrecall(k, v) {
    if(this !== obj) throw new Error(\"bad context\");
    return store[k];
    }
    }
    });

    /*
    * this allow you to iterate your \"private\"members in Object.prototype's methods
    */
    Object.defineProperty(obj, an.show, {
    get : function () {
    return function mkshow() {
    if(this !== obj) throw new Error(\"bad context\");
    return store;
    }
    }
    });

    };

    /**
    * to export
    */
    var wrapper = function(obj) {
    return storify(obj);
    };

    /* this could be redefined by the user (probably you) */
    wrapper.defaultAccessorNames = defaultAccessorNames;

    return wrapper;

    })(this);

    Hope it helps !

Contact Us

We'd love to hear from you. Get in touch!

Mail

P.O. Box 961436
Boston, MA 02196