Tuesday, April 1, 2014

Javascript Inheritance simplified

There has been a lot of posts recently about how JavaScript inheritance works, how it should be used and even what it should be called. The issue seems to be that the word inheritance comes with a lot of baggage. If we think about inheritance in the real world it is something that is passed down to the children. In JavaScript though it's more like everyone getting buried with their own property and going up the tree to find what you're looking for. Classical inheritance has the idea of classes which are the blueprint for how instances are made, which sounds an awful lot like a "prototype". So first off let me show you how I think JavaScript (prototypal/parasitic) inheritance works...

First thing we must do is forget all about classical inheritance, classes, constructors and even types beyond Objects (because when you have a class everything that is instantiated from it is of that "type" and we really don't have that in JavaScript).

So what is inheritance then? Well wikipedia says it's a form of code reuse. That is all it is. Forget about relationships between objects, parent & child, etc. for now. So how does this code reuse mechanism in JavaScript work? Well there are two things you need: A method to get to the code, and an actual path to get it.

Setting up the prototype chain (path)


var c = {};
var b = {
  '__proto__': c
};
var a = {
  '__proto__': b
};


That's all we need. The JavaScript standard calls it the [[prototype]] property but ES6 will standardize __proto__ soon. Most browsers now support it except for old IE. So I've provided a link back to other objects that may have the code I need. Now let's put some things in there:

Property access


var c = {
  a: 1
};
var b = {
  '__proto__': c,
  a: 2,
  c: {
    a: 3
  }
};
var a = {
  '__proto__': b
};


Now the method of getting things is easy. We use either dot or string property access (I'll just show dot property access from here on). It works kind of like this:

a.c.a; // 3


The first member of the accessor ('a') will get a variable on the scope (and will go up the different scopes until it finds it, but scope is another matter). okay we have our first access point, not the inheritance mechanism kicks in. The rule goes like this:


  1. is our property on this object? if yes return that
  2. is there a __proto__? if no return undefined
  3. go to the __proto__ and continue from 1
And you do that for each accessor. So for the above we get a then look for c. it's not there so we move to b which has a c. so now we've got the c object on b and we look for an a which it has got so we get 3. And that's all there really is to "inheritance" in JavaScript - the rest is just stuff that was bolted on to make it more like Java.

Setting properties


There is a common problem in AngularJS with scopes because they use this mechanism that has to do with prototypes. Let's say we have the below:

var a = {
  a1: 1,
  a2: { a3: 1 }
};
var aa = {
  '__proto__': a,
};
var ab = {
  '__proto__': a
};

// setting a property on the prototype chain

aa.a1; // 1
ab.a1; // 1
aa.a1 = 2;
ab.a1; // 1

// setting a property in an object on the prototype chain

aa.a2.a3; // 1
ab.a2.a3; // 1
aa.a2.a3 = 2;
ab.a2.a3; // 2

This will tend to confuse some people. Remember how I said that you use the above rule to access each property in turn? Well when setting a value you don't get the last value, you just set it directly on whatever object you're currently on. So in the first instance (aa.a1=2) we get aa but we don't go up the chain to find a1, we just set it directly on the object. You can see how this may get confusing though if we're going down the prototype chain one minute and then going in to an object and through it's chain another. This is why it pays to keep the inheritance simple and shallow.

How about functions?

In classical inheritance there are methods. In JavaScript there aren't. Putting a function on an object may give the cue it is meant to be used with the object but it doesn't mean it has to. With a function there is something called the context and all that means is that when that function is called is replaces "this" on the scope with the context. We retrieve the function through the same method as getting a property but we set the context to the object we got before the last access.


var a = {
  a: 1,
  getA = function() {return this.a};
};
var b = {
  a: 2,
  __proto__: a
};
a.getA(); // 1
b.getA(); // 2


In the second access we get the 'getA' function from 'a' but the context is set to the object that we had before the final property access which was 'b'. So the 'this.a' became 'b.a' which is 2.

Conclusion

And that's it. If you forget about all the constructors, prototypes on functions, poorly named stuff and baggage that comes along with it then inheritance in JavaScript is actually quite beautiful and simple. If you'd like to go more in depth then try reading getify's posts, his upcoming ebookthis one from ragonwald or my favourite that shows a great application of JavaScripts inheritance: AngularJS - Understanding Scopes

3 comments:

  1. __proto__ is deprecated:

    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto

    ReplyDelete
  2. proto is also part of the es6 draft spec

    ReplyDelete