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#).

var instance = {};
var what = Object.getPrototypeOf(instance) === Object.prototype; //true

[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.

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());

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:

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

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:

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;
    }
  };
})();

`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:

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.

The `revert()` method, in contrast, returns an object a given number of steps “up” the prototype hierarchy. (By default, this is 1.)

//assume the above code...
var _o2 = o3.revert();
_o2 === o2; //true
var _o1 = _o2.revert();
_o1 === o1; //true

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:

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

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).

function someImportantTask (user) {
  var elevated = user.elevate();
  if (elevated.isAdmin) {
    elevated.launchAllNukes();
  }
}

`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.

p.toString();
//"[4]::Walt -> [3]::Walter T. Williams -> [2]::Walter Williams -> [1]::Walter"

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)

9 thoughts on “Historical JavaScript Objects

  1. > In JavaScript, all objects have a `.prototype` property, which points to some object instance.

    Only functions have a .prototype property. All objects (and functions) have a [[Prototype]] *internal* property, which is what __proto__ represents. (__proto__ is convenient, but non-standard; ES5 provides Object.getPrototypeOf as a partial replacement.)

    function f() { }
    f.prototype.__proto__ === Object.prototype
    f.__proto__ === Function.prototype

    var o = new f();
    o.prototype // likely undefined
    o.__proto__ === f.prototype, if f.prototype is objectish (e.g. not null); else o.__proto__ === Object.prototype

    In other words, the `new` operator assigns the output’s [[Prototype]] internal property to the constructor function’s (public) `prototype` property (if it’s objectish).

    This is one of the most confusing parts of JavaScript.

    Also note that you can create an object-like value which is not `instanceof Object`:

    var o = Object.create(null);
    o instanceof Object === false
    ‘toString’ in o === false, even though ‘toString’ in Object.prototype
    o.__proto__ === null
    o.x = ‘y’; o.x === ‘y’

    I do like your approach and analysis. I don’t think of the concept as a *stack*, perse, but you do treat it line one. =]

    This reminds me of Haskell’s record manipulation. If you have a value `foo` with record `prop`, you can write:

    let foo’ = foo { prop = … }

    which will yield a new value, `foo’`, with `prop` changed. (Everything is immutable in Haskell, so `foo` cannot possibly be changed.)

    Why is `__version` decremented in `_popProto`? This looks like a bug. `_popProto` should literally be just `Object.getPrototypeOf`.

    It seems like you can centralize the methods of your `person` type by removing `getName` from the master prototype, and calling `setName` to create an instance.

    One problem in the back of my mind through all this (which you didn’t mention or notice) is the fact that the immutability is not necessarily transitive, which may lead to problems:

    historicalObject.create({
    array: [ ],
    add: function add(item) {
    var rev = this.revise();
    rev.array.push(item); // affects all instances!
    return rev;
    }
    });

    Thus, special care must be taken for historical objects. (For example, `rev.array = this.array.concat([ item ]);` can be used instead of `.push`.)

    As for applications of this technique, I have used it at least once (for configuration matching lexical scope (callbacks!)) with good results. I decided not to abuse prototypes for the sake of maintainability, however (though it wasn’t too difficult to understand, I feel).

  2. @strager, thanks for the great feedback! I updated the post to include your comment about the lack of `.prototype` on object literal instances. Thanks for clarifying this — it makes much more sense now.

    > Also note that you can create an object-like value which is not `instanceof Object`

    Yes, the `.create()` method has no logic to handle edge cases. It makes big assumptions, for the sake of the example.

    > Why is `__version` decremented in `_popProto`?

    `__version` is actually being mutated, as opposed to being re-created on every new object. When `_popProto()` is called, we are going backwards in the prototype chain, so the number gets decremented to reflect the “level” on the prototype chain where we are at. Not an elegant solution, mostly just for debugging.

    > the immutability is not necessarily transitive, which may lead to problems

    Yes! Very good point. Your array solution is spot on. The code assumes you will consciously write objects in this way. (Good thing this isn’t production code!)

  3. > > Also note that you can create an object-like value which is not `instanceof Object`

    > Yes, the `.create()` method has no logic to handle edge cases. It makes big assumptions, for the sake of the example.

    I was referring to Object.create, not your create. =] See the example which follows my comment.

    > > Why is `__version` decremented in `_popProto`?

    > `__version` is actually being mutated, as opposed to being re-created on every new object. When `_popProto()` is called, we are going backwards in the prototype chain, so the number gets decremented to reflect the “level” on the prototype chain where we are at. Not an elegant solution, mostly just for debugging.

    You aren’t modifying the master prototype; you’re modifying each instance:

    function create(proto) {
    function f() { }
    f.prototype = proto;
    return new f();
    }
    // or: var create = Object.create;

    var a = create({ v: 1 });
    var b = create(a);
    ++b.v;
    var c = create(c);
    ++c.v;

    ({ a: a.v, b: b.v, c: c.v })
    => { a: 1, b: 2, c: 3 }

    –a.v;

    ({ a: a.v, b: b.v, c: c.v })
    => { a: 0, b: 2, c: 3 }

    Recall that ++x.y; is equivalent[1] to x.y = x.y + 1; x.y is assigned on the newly created objects (b and c here).

    Here’s a demonstration of the problem in your implementation: http://slexy.org/view/s21ahUbsYf Notice how versions are fine until you start reverting things.

    [1] There are cases where this isn’t the case (e.g. if x.y is a string), but we’re only talking about numbers here so they’re equivalent.

  4. @strager — Thanks for pointing out that __version is actually being added to each object in the chain. I removed the decrementing operator, which fixes the problem you illustrated in your slexy.org example.

    I also updated the code examples to use Object.getPrototypeOf() instead of __proto__.

  5. kevin says:

    > This is a story of inheritance.

    Wrong statement. JavaScript is a prototype language based on delegation, this is very different from inheritance actually.

    > The prototype chain is conceptually similar to our understanding of inheritance in languages like C#.

    Hell no, such assumption is not going to help you.

    JavaScript is the world most misunderstood language for 2 reasons :

    - the least : It’s an object language based on prototypes
    - the most : It’s prototype implementation is quite awkward

    In class languages there’s 2 part, the class and the object (instance). (in some languages classes are objects though, can’t reasonably talk about this here)

    In prototype languages there’s just objects that describes themselves, nothing more. (some languages – all abandoned AFAIK – also described a lightweight class system too)

    Class instances shares the same behavior but should not modify themselves to live has a unique entity (at least in theory).

    Every prototype is an object and every object is a potential prototype, as such, every object is unique.

    Class languages have a mechanism known as inheritance that aims to “copy” properties from a parent class to a child class.

    There are two main types of prototype languages, those that uses cloning (shallow or deep) and those that implements delegation. JavaScript is based on delegation (though you can use cloning if you want to but it’s not “built-in”).

    A prototype is a description of a model object that’s meant to be replicated. If you do something like `Object.create(Person)` or `new Person()`. You’re not creating an instance, but an object which shares its default description with other persons, because immediate parent in the proto chain is a ref to Person.prototype. So if you modify Person, you instantly change the default description of all Person childs.

    Delegation prototype languages are based on differential inheritance/description (which is conceptually very different from what you might think of inheritance). It just means that when you create an object, you only (re)define what it has different than its parent, and that objects are a just more concrete description of a more general one.

    If JS feels like a Tim Burton movie it’s not much because it’s a proto language but because its implementation is weird (functions as prototypes, `new` keyword…). The real problem actually is that JS is a prototype language that since the start did not really respect the prototype philosophy, though ES5 brings some methods like `Object.create` that’s meant for this. That’s partly why some guys try to fake classes, but it’s a mistake because they try to add a layer of abstraction (and complexity). Prototype languages are not meant for abstraction.

    If you want to be an efficient JS dev, you should learn to **think prototype**. To do so, you may read papers on proto languages (late 80′s from Henry Lieberman are comprehensive but a bit old), read a bit about the Theory of prototypes https://en.wikipedia.org/wiki/Prototype_theory, and why not be aware too of what prototype pls “borrowed” from frame and actor languages.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>