Monday, April 22, 2013

Javascript prototypes, prototype chains

Creating a single object

That's easy, just use an object literal:
// a vector
var v = {
    x: 0,
    y: 0
}

Factory functions

Say you want to create several objects of the same kind. Then a factory function will do the trick:

Prototypes

So what exactly is Vector2D.prototype?

Every function object has a special attribute called the function's prototype. The default value is an empty object, and can be read and set.

function F(){}
F.prototype

in chrome it will be printed as F {}, meaning that it's an empty object, and is attached to F.
Now, given that is a regular object, we can add some data to it:

F.prototype.x = 0
// F {x: 0}

or even replace it completely with another object

F.prototype = { x: 1 }

On the other hand, every javascript object has an internal property which is called the object's prototype, which is not to be confused with Function.prototype.

In chrome, there's a read only attribute called __proto__ that allows examining the object's prototype.

So, what's the relationship between Function.prototype and object.__proto__?

If you create an object via a factory function, then this relation will hold:

var ob = new F;
ob.__proto__ === F.prototype

that is, using new with a function F, constructs an object which has F.prototype as its own prototype (__proto__).

The interesting part about prototypes is that if you try to access an attribute from a object, as in:

ob.x

Javascript does the following

1. First tries to find 'x' in the object itself.
2. Then in the object's prototype: ob.__proto__
3. Then in the object's prototype prototype: ob.__proto__.__proto__
4. etc
5. Until it reaches the topmost prototype, which is an empty object.

This looks very similar to class inheritance in a language with classes, right?

So, how can we setup an prototype chain as in #3?
Returning to our vector example, the current situation is this:

if
var v = new Vector2D;
then
v.__proto__ === Vector2D.prototype;

lets say we want to have Vector2D inherit from something more general, like an n-dimensional Vector:
in other words, we want that v.__proto__.__proto__ === Vector.prototype

In order to answer this, lets pose the question this way: If I have an object x, how can I get another object y which has x as its prototype?

y.__proto__ === x

There's an ECMAScript 5 function for doing this: Object.create():
var y = Object.create(x);
y.__proto__ === x;
And this is all we need to setup a prototype chain. Of course we need to do this *before* start adding methods to Vector2D.prototype, otherwise they'll be overwritten:
this have the interesting effect that Javascript knows about v2's constructors:
v2 instanceof Vector2D === true;
v2 instanceof Vector === true;

No comments:

Post a Comment