JS101: __proto__

26 Nov 2012 | By Alex Young | Tags js101 tutorials language beginner

When I originally wrote about prototypes in JS101: Prototypes a few people were confused that I didn’t mention the __proto__ property. One reason I didn’t mention it is I was sticking to standard ECMAScript for the most part, using the Annotated ECMAScript 5.1 site as a reference. It’s actually hard to talk about prototypes without referring to __proto__, though, because it serves a very specific and useful purpose.

Recall that objects are created using constructors:

function User() {
}

var user = new User();

The prototype property can be used to add properties to instances of User:

function User() {
}

User.prototype.greet = function() {
  return 'hello';
};

var user = new User();
user.greet();

So far so good. The original constructor can be referenced using the constructor property on an instance:

assert.equal(user.constructor, User);

However, user.prototype is not the same as User.prototype. What if we wanted to get hold of the original prototype where the greet method was defined based on an instance of a User?

That’s where __proto__ comes in. Given that fact, we now know the following two statements to be true:

assert.equal(user.constructor, User);
assert.equal(user.__proto__, User.prototype);

Unfortunately, __proto__ doesn’t appear in ECMAScript 5 – so where does it come from? As noted by the documentation on MDN it’s a non-standard property. Or is it? It’s included in Ecma-262 Edition 6, which means whether it’s standard or not depends on the version of ECMAScript that you’re using.

It follows that an instance’s constructor should contain a reference to the constructor’s prototype. If this is true, then we can test it using these assertions:

assert.equal(user.constructor.prototype, User.prototype);
assert.equal(user.constructor.prototype, user.__proto__);

The standards also define Object.getPrototypeOf – this returns the internal property of an object. That means we can use it to access the constructor’s prototype:

assert.equal(Object.getPrototypeOf(user), User.prototype);

Putting all of this together gives this script which will pass in Node and Chrome (given a suitable assertion library):

var assert = require('assert');

function User() {
}

var user = new User();

assert.equal(user.__proto__, User.prototype);
assert.equal(user.constructor, User);
assert.equal(user.constructor.prototype, User.prototype);
assert.equal(user.constructor.prototype, user.__proto__);
assert.equal(Object.getPrototypeOf(user), User.prototype);

Internal Prototype

The confusion around __proto__ arises because of the term internal prototype:

All objects have an internal property called [[Prototype]]. The value of this property is either null or an object and is used for implementing inheritance.

Internally there has to be a way to access the constructor’s prototype to correctly implement inheritance – whether or not this is available to us is another matter. Why is accessing it useful to us? In the wild you’ll occasionally see people setting an object’s __proto__ property to make objects look like they inherit from another object. This used to be the case in Node’s assertion module, but Node’s util.inherits method is a more idiomatic way to do it:

// Compare to: assert.AssertionError.__proto__ = Error.prototype;
util.inherits(assert.AssertionError, Error);

This was changed in assert: remove unnecessary use of __proto__.

The Constructor’s Prototype

The User example’s internal prototype is set to Function.prototype:

assert.equal(User.__proto__, Function.prototype);

If you’re about to put on your hat, pick up your briefcase, and walk right out the door: hold on a minute. You’re coming to the end of the chain – the prototype chain that is:

assert.equal(User.__proto__, Function.prototype);
assert.equal(Function.prototype.__proto__, Object.prototype);
assert.equal(Object.prototype.__proto__, null);

Remember that the __proto__ property is the internal prototype – this is how JavaScript’s inheritance chain is implemented. Every User inherits from Function.prototype which in turn inherits from Object.prototype, and Object.prototype’s internal prototype is null which allows the inheritance algorithm to know it has reached the end of the chain.

Therefore, adding a method to Object.prototype will make it available to every object. Properties of the Object Prototype Object include toString, valueOf, and hasOwnProperty. That means instances of the User constructor in the previous example will have these methods.

Pithy Closing Remark

JavaScript’s inheritance model is not class-based. Joost Diepenmaat’s post, Constructors considered mildly confusing, summarises this as follows:

In a class-based object system, typically classes inherit from each other, and objects are instances of those classes. … constructors do nothing like this: in fact constructors have their own [[Prototype]] chain completely separate from the [[Prototype]] chain of objects they initialize.

Rather than visualising JavaScript objects as “classes”, try to think in terms of two parallel lines of prototype chains: one for constructors, and one for initialised objects.

References


blog comments powered by Disqus