JavaScript: __proto__

[2012-10-03] esnext, dev, javascript, __proto__, jslang
(Ad, please don’t block)
[This post is part of a series on the special property __proto__]

This blog post looks at the special property __proto__, which allows you to get and set the prototype of an object. In order to understand this post, you should be familiar with JavaScript’s prototypal inheritance [1].

The special property __proto__

The ECMAScript standard specifies objects as pointing to their prototype via the internal property [[Prototype]]. That property cannot be directly modified in ECMAScript 5, but one can use Object.getPrototypeOf() to read it and Object.create() to create new objects that have a given prototype. For example, the following code creates an object obj whose prototype is myProto.
    > var myProto = {};
    > var obj = Object.create(myProto);

    > Object.getPrototypeOf(obj) === myProto
    true

__proto__ (pronounced “dunder proto”, from “double underscore” [2]) first appeared in Firefox and is an alias for [[Prototype]]. Using __proto__, the above code becomes:

    > var myProto = {};
    > var obj = { __proto__: myProto };

    > obj.__proto__ === myProto
    true
The following also holds:
    > obj.__proto__ === Object.getPrototypeOf(obj)
    true
Once it appeared in Firefox, __proto__ proved so popular that it is now also supported by V8 (Chrome, Node.js) and Nitro (Safari). As of ECMAScript 5, it is still non-standard, but due to its popularity, it will become part of ECMAScript 6.

Checking whether __proto__ is supported

The following expression returns true if a JavaScript engine supports __proto__:
    Object.getPrototypeOf({ __proto__: null }) === null

Caveat: objects as maps

If you use an object as a map from strings to values (with arbitrary keys) then you have to be careful about keys whose value is "__proto__" [3]. For example:
    function escapeKey(key) {
        // We need to escape "__proto__", including the
        // (n times) escaped version of it, to avoid clashes
        if (key.indexOf("__proto__") === 0) {
            return key+"%";
        } else {
            return key;
        }
    }
Obviously, you have to escape keys for both read and write access. Thus, you never need to un-escape.

Two use cases

Two important use cases for __proto__ are: Creating objects with a given prototype and subtyping built-in types.

Creating objects with a given prototype

The usability advantage of __proto__ becomes apparent if you create a non-empty object:
    var obj = {
        __proto__: myProto,
        foo: 123,
        bar: "abc"
    };
With Object.create(), you have two, equally unappealing, alternatives:
    // Alternative 1: create empty object, assign
    var obj = Object.create(myProto);
    obj.foo = 123;
    obj.bar = "abc";

    // Alternative 2: property descriptors
    var obj = Object.create(myProto, {
        foo: {
            value: 123,
            writable: true,
            enumerable: true,
            configurable: true
        },
        bar: {
            value: "abc",
            writable: true,
            enumerable: true,
            configurable: true
        }
    });

Subtyping built-in types

JavaScript’s built-ins are notoriously hard to subtype [4], for two reasons. First, the standard subtyping pattern in JavaScript is that a sub-constructor initially passes its instance to the super-constructor so that it can add its properties. It does so by calling the super-constructor as a function with this pointing to the instance. Alas, most built-in constructors don’t let you do that, they ignore a passed-in this. Second, some built-ins have instances that are special, so even if you could pass the sub-instance to the super-constructor, you would never end up with a working instance. The main example is arrays, whose length property is updated if new elements are added.
    var MyArrayProto = Object.create(Array.prototype);
    MyArrayProto.foo = function (...) { ... };
    function createMyArray() {
        var arr = Array.prototype.slice.call(arguments);
        arr.__proto__ = MyArrayProto;
    }
    var myarr = createMyArray();

ECMAScript 6

As mentioned before, ECMAScript 6 will include __proto__. At the very least, you’ll be able to use it to retrieve the prototype of an object and to set the prototype in an object literal. The combination of object literal plus __proto__ can be considered a more user-friendly Object.create() that is good enough for most use cases.

It is not yet certain that you’ll also be able to change the prototype of an existing object via __proto__. The most important use case for that is to subtype Array, which can be better supported by other means, e.g. via a function Array.createArray(proto).

Lastly, ECMAScript 6 will probably also provide ways for switching off __proto__ for some objects, possibly even for all objects.

More material on the web

References

  1. Prototypes as classes – an introduction to JavaScript inheritance
  2. How to pronounce __proto__
  3. The pitfalls of using objects as maps in JavaScript
  4. Subtyping JavaScript built-ins