Writings.

14.04.10

Private and protected fields in JavaScript

Information hiding is an important technique in building maintainable software systems; by providing access to only the minimal interface of a module, changes can be made to the internal implementation without fear of breaking other modules that use it. Many object-oriented programming languages such as Java and C++ provide private and protected methods and fields to help enforce this; a private field can only be accessed by methods in the class in which it is defined, protected ones can also be accessed by methods in subclasses. There have been a numberofattempts to add private and protected methods and fields to JavaScript objects, but all are either easily circumvented or clumsy and inefficient. For example, the Mootools implementation of protected methods allow them to be called if there is a method from the class anywhere in the call stack. What does this mean in plain(er) English? Any callback function provided to a method will be able to access the protected methods in that class:

Douglas Crockford adds private methods and variables by defining them in the constructor, but this is both inefficient (creating a new function object every time the constructor function is called) and means they cannot be accessed by any methods added subsequently to the constructor prototype or by methods defined on subclasses. What we want is to be able to define classes a bit like this:

Is this possible? Well, surprisingly yes, but sadly not in all browsers. Still, there are some interesting things to look at. The code is presented below, with comments to help explain some of the trickier points. The general class function is fairly similar to that in Mootools, which itself was originally, I believe, based on Dean Edwards base2. However, there are significant differences. For example, Mootools wraps every function (and the constructor) in another function; my version wraps neither, thereby reducing the number of function calls, which should be more efficient. The only compromise that must be made because of this is that calls to this.parent (i.e. a call to an overridden function in the parent class) must include the name of the method as an extra first argument. I do not consider this a significant issue, but feel free to disagree (yes, it does allow you to call an overridden method with a different name to the calling method; this could be considered a bonus or a disadvantage depending on how you look at it). There is a lot to take in here, so if youre not comfortable with the concept of closures, prototypes and the call stack, you might need to do a little research elsewhere.

Now, compatibility. As shown above, the code works in Firefox (at last v3.0+, possibly v2+), Chrome (at least the latest version, I've not tested on earlier ones) and Opera 10.5. Notably missing are of course IE and Safari. The reason for this is that two non-standard constructs are being used. First up is the use of a functionss caller property; whilst not a part of the ECMA standard it nevertheless has been supported by every major browser (even IE) for some time. The other interesting construct is the use of getters and setters. Actually these have now been standardised, but the new syntax defined in the standard (Object.defineProperty) is currently only supported by IE8 and then only for DOM elements, which is useless in this context. All the other major browsers support the syntax used above though, even though it is not the one standardised, so why the lack of support from Safari and Opera pre 10.5? The answer is that these browsers fail to set the caller property on the getter/setter functions. I'd say this is a bug, but given it's the interaction between two non-standardised attributes thats not really fair!

So is this all useless? No, not really. First of all, the technique could be applied without getters and setters to support private and protected methods (but not fields) by just replacing the getter with a standard function wrapper; this is still better than the solutions we have today as it avoids the callback problem but you wont get the additional getter/setter benefit of preventing outside functions overwriting private/protected fields. However, over the next few years we should see browsers standardise the getter/setter syntax, leaving just the caller property to worry about. If that can get incorporated into the standard too, we could be on to a winner.

Update: Thanks to Tobie Langel for reminding me to point out that the caller property of functions not only isnt standardised, but in ECMAScript 5 strict mode is an explicit error. I have yet to see a convincing explanation as to why this property is a security hole (which is the reason always given for its removal). The functionality is not replaceable with other JavaScript constructs (unlike arguments.callee which can be replaced by naming anonymous functions) and is so useful, I hope that it may make a reappearance in the spec as a valid property. Given the widespread support in browsers, it is a de-facto standard and unlikely to disappear soon, although I can understand people wanting to avoid it. I am not saying that this is the ultimate solution to information hiding in JavaScript, but it is an interesting approach and hopefully by providing examples such as this of useful features which cannot be implemented any other way, the standards committee may reconsider the caller issue; it has already been removed and resurrected once in Mozilla. To quote Brendan Eich (creator of JavaScript) from 2001: “All traces of a caller property were removed a while ago, to follow ECMA-262 and to avoid any chance of a security exploit. The removal seems to me to have been overzealous, because it axed the caller property of function objects *and* the much-more-exploitable caller (or __caller__) property of activation objects.”

Another update: and just for the hell of it, I've adapted the code to not use the caller property: see this post. All of the same features described above are supported, along with Safari and Opera 10.10.