Inheritance in JavaScript
JavaScript adopts a radically different take on inheritance from what one is perhaps used to coming from an object oriented, statically typed language world such as C++, C# or Java. In the typical OO static language, inheritance is applied to "classes" which are essentially templates for how an object instance should be laid out in memory at runtime. JavaScript however is weakly typed, i.e. pretty much everything is an "object". How will inheritance work out in a weakly typed dynamic language such as JavaScript? Enter "prototypal inheritance"!
Prototypal Inheritance
Somewhat counter-intuitively, inheritance in JavaScript applies to object instances instead of types. I know that sounds kind of weird but once we see how it works, it actually turns out to be quite elegant. Every object has a hidden default member called its "prototype". The prototype of an object is a simple reference to another object which in turn has its own prototype reference to yet another object. Here's a picture illustrating this:
Here, object1
's prototype points to object2
and object2
's prototype points to the prototype of the JavaScript Object
type (part of the JavaScript library). The Object
type's prototype does not in turn point to yet another object and is always null
thereby making it the root object in the inheritance hierarchy.
Whenever you attempt to access a member field or method on an object the JavaScript runtime attempts to locate the member directly on the object. If the lookup fails then it attempts to locate the member in the object's prototype and continues to walk the prototype chain till the member is found (if the member ends up never being found then you get a syntax error or an "undefined" reference depending on the context). Here's a pseudo-implementation of member lookup implemented in JavaScript:
//
// dereferences the member identified by "memid"
// if it exists directly on the object; returns
// "null" otherwise
//
Object.prototype.getOwnProperty = function (memid) {
if (this.hasOwnProperty(memid))
return this[memid];
return null;
};
//
// lookup member property/method called "memid" on object
// "obj"; walks the prototype inheritance chain till member
// is found; returns "null" if member is not found
//
function lookup(obj, memid) {
var mem = obj.getOwnProperty(memid);
while (!mem) {
obj = Object.getPrototypeOf(obj);
if (!obj)
break;
mem = obj.getOwnProperty(memid);
}
return mem;
}
So how does prototypal inheritance work?
So how exactly do you do prototypal inheritance? The basic idea is that new objects are created by copying the structure of an existing prototype object (see prototype creational design pattern). Here's another bit of JavaScript pseudo-code that shows how this might work:
var person_proto = {
name: "no name",
age: 0
};
var o = {};
o.prototype = person_proto; // this won't actually work!
Please note that the above actually won't work the way you intend it to if you run it (it will simply create a new property called "prototype" on "o
"!). The object "o
"'s prototype will not get set. In fact JavaScript does not provide a mechanism to assign a value to an object instance's prototype (Mozilla and WebKit browsers do however permit this via a special extension property named __proto__
- but this is not a part of the ECMAScript standard and should not be relied upon). Having said that, in essence, what has been given above is what happens when you inherit prototypally. Here, "o
" is a new object whose prototype is a reference to another object (person_proto
). When you access the name
property like so:
print(o.name);
The JavaScript engine would actually resolve the "name" property on the object's prototype since the object itself does not have a property called "name". But since the JavaScript language does not allow one to assign the prototype on an object instance how can we achieve this sort of inheritance in a standards compliant manner? Douglas Crockford has discussed precisely this in his blog post on prototypal inheritance. His solution basically takes advantage of the fact that while you cannot assign the prototype of an object instance you can in fact assign the prototype on a Function
object. Here's a utility routine that allows us to inherit prototypally from another object.
function inherit(o) {
function F() {}
F.prototype = o;
return new F();
}
And here's some code that shows how we'd use this function to inherit from the person_proto
object defined in the previous snippet:
var gender_person = inherit(person_proto);
gender_person.gender = "f";
Note that I have also extended the gender_person
object by tacking on the gender
property to it. Imagine that I wish to inherit another object from gender_person
and add a "married
" boolean member field to it. Here's how I'd go about it.
var marital_person = inherit(gender_person);
marital_person.married = false;
Here's the inheritance chain depicted pictorially:
Note that the inheritance relationship is completely between object instances and not types. Also note the following aspects of this style of programming:
- Each "inherit" operation is essentially an object instantiation.
- Each "inherit" seems to be always accompanied with the definition of new members on the new object.
ECMAScript version 5 therefore adds a new function called Object.create
that does exactly this. Here's the same example done using Object.create
instead of our homebrewed inherit
.
//
// create basic person prototype object; this
// object's prototype will be equal to Object.prototype
//
var person_proto = {
name: "no name",
age: 0
};
//
// inherit from "person_proto" and add a new member
// field called "gender"
//
var gender_person = Object.create(person_proto, {
gender: {
value: "f",
writable: true,
enumerable: true,
configurable: true
}
});
//
// inherit from "gender_person" and add a new member
// field called "married"
//
var marital_person = Object.create(gender_person, {
married: {
value: false,
writable: true,
enumerable: true,
configurable: true
}
});
for(var i in marital_person)
print(sprintf("marital_person.%s = %s", i, marital_person[i]));
Here's the output we get when we run this snippet (you can try it in the Eval console yourself if you like - just make sure that you're running a browser that has support for ECMAScript 5 - which is most modern browsers).
marital_person.married = false
marital_person.gender = f
marital_person.name = no name
marital_person.age = 0
Its interesting to observe that when you use for...in
to reflect on an object it seems to walk the prototype chain bottom up. In the output above, it printed the properties on marital_person
first, then gender_person
and finally proto_person
. This prototypal nature of the language is what enables you to do powerful things such as adding new members to even core library types such as Object
, String
, Array
etc. When you add a new trim
function to String.prototype
for instance, it automagically shows up not just on new string objects created henceforth but on all string instances that are alive at that point in time! This is made possible because during the actual member resolution process the runtime walks the prototype chain to locate it before giving up.
But I want my new!
What I have described so far is the way inheritance was designed to work in JavaScript. Desiring to cater to developers who are familiar with static languages such as Java (which was really taking off at the time JavaScript was introduced), Netscape decided to make the unfortunate choice of simulating classical object instantiation and type inheritance behavior by introducing syntactic sugar (or maybe it should be called syntactic salt!) that hid the prototypal nature from the programmer. The end result is a construct that ends up being not quite classical inheritance while deliberately misleading the developer on what's actually happening under the covers! Its fairly common to see code such as the following today:
function Person(n, a) {
this.name = n;
this.age = a;
}
var p = new Person("Foo", 10);
Its interesting to see what is actually happening under the covers when you "new" up an object. Here's the last line of the snippet given above "expanded" to the actual instructions being run under the covers in pseudocode form.
var p = {}; // create empty object
p.prototype = Person.prototype; // assign prototype (as before
// this won't actually set the
// prototype)
Person.call(p, "Foo", 10); // run constructor on object
Briefly, an empty object gets created and its prototype gets assigned to Person
's prototype and the constructor function is invoked using the new object as its context. It is self-evident that there really isn't a "type" called "Person
". The language just allows the developer to pretend like there is! In fact it even provides an "instanceof
" operator that you can use to verify if an object is an instance of a particular "type". Here's how you use it
function Person(n, a) {
this.name = n;
this.age = a;
}
var p = new Person("Foo", 10);
print(p instanceof Person); // prints "true"
What the operator does is to check if the prototype of the constructor function (Person
in the snippet above) figures in the object's prototype chain. Here's a possible naive implementation of the "instanceof
" operator as a JavaScript function with some sample usage:
function instanceOf(o, t) {
if(typeof(t) !== "function")
throw "Pass a Function object for type name.";
if(typeof(o) !== "object")
throw "Object must not be a primitive type.";
var proto = t.prototype;
var op = Object.getPrototypeOf(o);
while(op) {
if(proto === op)
return true;
op = Object.getPrototypeOf(op);
}
return false;
}
function Person(n, a) {
this.name = n;
this.age = a;
}
var p = new Person("Foo", 10);
print(instanceOf(p, Person)); // prints "true"
With that we conclude our brief review of how inheritance was designed to work in JavaScript. I have deliberately not discussed how we can simulate classical inheritance patterns in JavaScript though that is very much possible. Douglas Crockford has covered this topic in this article but he ended up adding the following footnote to it - presumably many years after he'd written that article (emphasis mine):
I have been writing JavaScript for 8 years now, and I have never once found need to use an
uber
function. Thesuper
idea is fairly important in the classical pattern, but it appears to be unnecessary in the prototypal and functional patterns. I now see my early attempts to support the classical model in JavaScript as a mistake.