Historical JavaScript Objects

For the last several weeks I have been trying to “think like JavaScript”. If JavaScript were a movie, Tim Burton would direct and his token boy Johnny Depp would be the lead, most likely wearing an outlandish costume and twirling mammoth daisies or something. At least, this is how I see it in my mind. JavaScript objects are… flexible. Contortionist flexible. Its actually a little scary. So I decided that, if I were truly going to be productive with this language, I would need abuse the hell out of them to find out where exactly their limits lie. I am going to warn you now: my latest experiment is exactly that. If you find it interesting, enticing, or perhaps seductive, you bear all the risks of its use! I will have none of your complaints!

“For this I had deprived myself of rest and health. I had desired it with an ardour that far exceeded moderation; but now that I had finished, the beauty of the dream vanished, and breathless horror and disgust filled my heart.” -- Doctor Frankenstein, shortly after he finished his JavaScript example

This is a story of inheritance. In JavaScript, all objects have a .prototype property, which points to some object instance. When an object literal (or hash) is created, the .prototype property defaults to `Object.prototype`, a default object instance with very few members (think of this as the “Object” object in C#). [javascript] var instance = {}; var what = Object.getPrototypeOf(instance) === Object.prototype; //true [/javascript] _[EDIT: As @stranger pointed out in the comments at the end, object literals do not have a .prototype property. Most browsers provide a non-standard .\_\_proto\_\_ convenience property, which can be used to access the prototype object for both literals and function objects.]_ Another way to create object instances in JavaScript is to use constructor functions with the `new` keyword. These functions also have a .prototype property which can be set to an instance of an object literal. This is typically done before any instance objects are created from the function constructor. All instances created from the function constructor will share the prototype instance, and it will be accessible via the prototype chain when instance members are invoked. In this way, constructor function instances “inherit” the members of their prototype object. [javascript] function Hipster () { this.shout = function () { return “I WROTE JAVASCRIPT BEFORE IT WAS COOL!”; }; } Hipster.prototype = { speak: function () { return ‘My rainbow scarf complements my skinny jeans.’; } }; var hipster = new Hipster(); alert(hipster.shout()); alert(hipster.speak()); [/javascript] The prototype chain is superficially similar to our understanding of inheritance in languages like C#. We are pretty familiar with this mode of thinking. The tricky thing is remembering that the prototype property is set on a constructor function, not on an instance object. In JavaScript, the prototype chain can extend quite a ways. When you access a member on an object, JavaScript will look “up” the prototype chain until it either finds a member with a matching name, or it bottoms out at `Object.prototype`, the instance to which all objects are ultimately tied. If an instance object and its prototype have members of the same name, the instance member is used because it satisfies the prototype chain query first. So this is all nice, but what does it have to do with the abomination I’m about to bring to life? Simple. It dawned on me while I was mowing the lawn yesterday that a JavaScript prototype chain kind of resembles a stack. When you add an item to the prototype chain, you are pushing an object onto the stack; when you remove the last object in the prototype chain, you are popping an object off the stack. And since objects in the prototype chain can have members with the same names, this simple stack can effectively be used to “version” an object’s state and behavior over time. I like to imagine the public behavior (API) of objects that I create before I start actually coding, so I started with some bogus code that represents how I imagine someone might use this amazing technological breakthrough: [javascript] var p = person(‘Walter’) .setName(‘Walter Williams’) .setName(‘Walter T. Williams’) .setName(‘Walt’); alert(p.getName()); //Walt p = p.revert(); alert(p.getName()); //Walter T. Williams p = p.revert(2); alert(p.getName()); //Walter [/javascript] The example is simple. Every time .setName() is called, the object is revised, but the prototype chain is used, behind the scenes, to keep track of previous versions of the object, which we can then access at a later point in time by calling .revert(). How does this magical goodness happen? To wit: [javascript highlight=”3,15,26,29”] var historicalObject = (function () { var _pushProto = function (obj) { function EmptyObject () {} EmptyObject.prototype = obj || Object.prototype; var other = new EmptyObject(); if (other.__version !== undefined) { other.__version++; } else { other.__version = 1; } return other; }; var _popProto = function (obj) { var proto = Object.getPrototypeOf(obj); if (proto === Object.prototype) { return obj; } return proto; }; return { create: function (base) { var obj = _pushProto(base); obj.revise = function () { return _pushProto(this); }; obj.revert = function (howFar) { if (typeof howFar !== ‘number’) { howFar = 1; } var other = _popProto(this); howFar–; if (howFar === 0) { return other; } return other.revert(howFar); }; return obj; } }; })(); [/javascript] `historicalObject` is an instance with a single method: .create(). This method accepts an object literal and invokes the \_pushProto() method (line 3) that 1) creates a dummy constructor function, 2) sets the object literal as its prototype, and 3) creates an instance of the dummy constructor. (I also assign a \_\_version property which is mostly just for debugging purposes, so the “version number” of the object can be queried.) In the .create() function, this new instance (obj) is then given two public methods: `revise()` and `revert()`. `revise()` is straight forward – it takes its current value of `this` and passes it to \_pushProto(), which will then create a new object with `this` as its prototype. So we would end up with something like this: [javascript] var o1 = historicalObject.create({ /*some properties*/ }); var o2 = o1.revise(); Object.getPrototypeOf(o2) === o1; //true var o3 = o2.revise(); Object.getPrototypeOf(o3) === o2; //true //etc. [/javascript] The `revert()` method, in contrast, returns an object a given number of steps “up” the prototype hierarchy. (By default, this is 1.) [javascript] //assume the above code… var _o2 = o3.revert(); _o2 === o2; //true var _o1 = _o2.revert(); _o1 === o1; //true [/javascript] I decided to create an implementation of `person` that would take advantage of this “prototype-as-stack” concept and roughly match my imagined usage example from earlier. Here is the implementation: [javascript highlight=”5,6,7,8,19,20,21,22,23”] var person = function (name, permissions) { return historicalObject.create({ setName: function (name) { var other = this.revise(); other.getName = function () { return name; }; return other; }, getName: function () { return name; }, elevate: function () { if (!permissions.indexOf(‘admin’) < 0) { return; } var _this = this; var other = this.revise(); other.isAdmin = true; other.toString = function () { return “(admin) “ + _this.toString(); }; return other; }, toString: function toString () { var str = “[“ + this.__version + “]::” + this.getName(); var proto = Object.getPrototypeOf(this); if (proto.hasOwnProperty(‘__version’)) { str += ‘ -> ‘ + proto.toString(); } return str; } }); }; var p = person(‘Walter’, [‘admin’]) .setName(‘Walter Williams’) .setName(‘Walter T. Williams’) .setName(‘Walt’); console.log(p.getName()); //Walt p = p.revert(); console.log(p.getName()); //Walter T. Williams p = p.elevate(); console.log(p.toString()); //(admin) … p = p.revert(); console.log(p.toString()); //no admin [/javascript] When `person()` is invoked, it returns an instance created by `historicalObject` with some initial data passed as function parameters (name, permissions). The instance has several public methods: `setName()`, `getName()`, `elevate()`, and `toString()`. The name mutators are fairly straightforward, except for the fact that I’m not tracking name as an instance variable anywhere. Notice on line 5 that I am adding to the prototype chain by calling `this.revise()`, and then on lines 5-8 I am replacing the `getName()` method altogether using the closure to access the current value for `name`. Since the old object (which will be the new prototype object) still has a copy of the last `getName()` method, when we revert the object, the old name value will be accessible again. Since the prototype chain notices that the new object has its own `getName()` method, however, it will invoke it on the new instance, `other`. The `elevate()` method is also interesting, in that it adds entirely new behavior to the newest member of the prototype chain. On line 19, you can see where I am creating `other` and making its prototype the current instance, `this`. I then add an `isAdmin` property and create a new `toString()` method that prepends the string “(admin)” to the value of `_this.toString()`. (I could have used Object.getPrototypeOf(this).toString() because the old object which is being versioned would be the value of the prototype on the new object; I decided to use the closure instead for clarity.) These new members seem trivial here, but I could easily imagine some security-related behavior being added to the new instance at this point – behavior that will vanish once `revert()` is called. `elevate()` acts as a kind of `sudo` (in the Linux world). [javascript] function someImportantTask (user) { var elevated = user.elevate(); if (elevated.isAdmin) { elevated.launchAllNukes(); } } [/javascript] `toString()` is relatively unimpressive; it just walks the prototype chain and concats the values of all `toString()` methods it encounters. I used this primarily to visualize the prototype chain, and to see the version numbers associated with each object. [javascript] p.toString(); //“[4]::Walt -> [3]::Walter T. Williams -> [2]::Walter Williams -> [1]::Walter” [/javascript] Something else you probably noticed is that the only time a new object is returned from a method is when the method “mutates” object state (e.g., `setName()` and `elevate()` in this example). This is really because objects only need be versioned when their data or behavior change. Since we aren’t actually changing the data inside of these objects, each instance is pretty much immutable. This also creates a pseudo-fluent API, but only for certain methods. Even though this is the most awesome thing you’ve ever seen (you know it is), I can immediately see some issues that might arise if one were to actually use this monstrosity. First, since object mutations are really generating new objects, you have to be careful about holding onto references or else there will be memory leaks. Second, if you are passing around references, you want to make sure that they are really assigned to the objects you think they are assigned to. It would do no good for you to elevate a user, then revert the object, but continue using the reference to the elevated object! This could end badly. I’m sure there are other problems here – this is merely an exploratory effort with no real vetting effort put into it. I found it entertaining, educational, and a bit twisted, and my only hopes are that you do as well. If it breaks your shit its your own damn fault; you have been WARNED. (full code example here)