Douglas Crockford refers to JavaScript as “Lisp in C’s Clothing” which gives us an inkling to it’s true power. Unfortunately JavaScript, a language that started with a rushed schedule to production and a specification that was buffeted with strong political winds at it’s inception, has often been “misunderstood”. Despite it’s unfortunate beginnings, amidst the stormy waters laden with many a pitfall like global variables, lie a few pearls of wisdom, of which one is prototypal inheritance. In this 2-series article we will take a deep dive into JavaScript’s prototypal nature - we will see how it works, and more importantly how we, as JavaScript developers can leverage it. We will take it a step further to see how we can develop our own hierarchies to model our code, allowing for better reuse.
This is an article I wrote for NFJS, The Magazine’s April, 2012 issue. This is a 2-part series, this being the first one - I will post the second one soon. Stay tuned!
Gone are the days when we, as developers could get away with one-off scripts to power our web pages. With the advent of rich, and highly interactive web applications, more and more “core” logic has shifted to the client making JavaScript a first class language within our code-base, and warranting such treatment. Developers are constantly looking to better model their code to reflect real world scenarios, in which inheritance plays an important role. JavaScript does provides you the ability to create rich and deep hierarchies, but does so in a fashion that is not immediately obvious to those coming from a language like Java. To better understand how we can use this valuable feature, we will need to dig past the surface, and examine how JavaScript uses objects, and functions in unison to make this happen.
I highly recommend that you play along with the code as you read this, but you will need a runtime. I suggest you use Google Chrome since it comes bundled with a JavaScript console. Just open up a new tab, right-click anywhere on the page, and select “Inspect Element”. A small window should pop open at the bottom of the page, with it’s own toolbar. The last item on the tool bar is called “Console” - Clicking that will bring you in to the JavaScript console. Another suggestion is to click the icon at the bottom-left of the JavaScript console (The tooltip says “Undock into a seperate window.") - This detaches the Code Inspector from the main window and should give you much more real estate to play with^1 .
Ready? Let’s go find us some inheritance …
Objects
Fundamentally, JavaScript is object-oriented. To those familiar with JavaScript this should come as no surprise. JavaScript comes bundled with a few “global” objects, such as Array, Boolean, Date, String and Object. Instances of these are adorned with certain properties, and behaviors, such as [].length
(which is the length
of an array), or "javascript".toUpperCase()
(which returns "JAVASCRIPT"
).
There is a subtle, yet important distinction here that I must highlight, and that will be relevant to our on-going discussion. [].length
is merely looking up the value of a property in the array, much like you would look up a public
variable on an object in Java. On the other hand, "javascript".toUpperCase()
is invoking a method on the String
instance. This pattern of invocation is referred to as the “Method Invocation Pattern”, in that you are invoking a function as a method of an object^2 .
There is another way to invoke a function, as shown in Listing 1
1
2
3
4
5
6
function sayHelloTo(name) {
return "Hello " + name + "!";
}
console.log(sayHelloTo("Brendan")); // evaluates to "Hello Brendan!"
This is called the “Function Invocation” pattern because it seems that you are invoking a stand-alone function. This is not entirely correct - rather the function is attached to the “global” object, which in the case of your browser is the window
object.
Creating your own objects
JavaScript allows you to create your own objects, and offers several ways to do so, each offering something subtly different. The easiest way to create an object is to use the literal syntax - {}
. You can then attach properties and methods to this object, and you can look these properties up, or invoke these methods just like you would any JavaScript supplied object. Let’s take a look in Listing 2
1
2
3
4
5
6
7
8
9
10
11
var myObj = {};
myObj.name = "Some Object";
myObj.sayHello = function(name) {
return "Hello " + name + "!";
};
// try it out
console.log(myObj.name); // evaluates to "Some Object"
console.log(myObj.sayHello("Brendan")); // evaluates to "Hello Brendan!"
You can declare an object, and all the properties and methods in one fell swoop, as shown in Listing 3
1
2
3
4
5
6
7
8
9
10
11
var myObj = {
name : "Some Object", // NOTE the : and the ',' at the end
sayHello : function(name) {
return "Hello " + name + "!";
}
};
// try it out ... again
console.log(myObj.name); // evaluates to "Some Object"
console.log(myObj.sayHello("Brendan")); // evaluates to "Hello Brendan!"
Notice that instead of =
signs we use :
, and we separate the property definitions using commas as delimiters.
There is another way to define your own object, and that is to use the create
method defined on Object
. This method takes an argument, the meaning of which we will see in Part II of this series, but for now a null
will suffice^3 . Let’s take a look (Listing 4)
1
2
3
4
5
6
7
8
9
10
11
var myObj = Object.create(null);
myObj.name = "Some Object";
myObj.sayHello = function(name) {
return "Hello " + name + "!";
};
// try it out
console.log(myObj.name); // returns "Some Object"
console.log(myObj.sayHello("Brendan")); // returns "Hello Brendan!"
Now that we know how to create our own objects, it should be noted that even plain vanilla objects that you create, say with the literal {}
syntax, have a few properties and functions magically defined on them. See Listing 5.
1
2
3
4
5
6
7
8
9
10
11
12
13
var obj = {};
console.log(obj.constructor); // returns function Object()
console.log(obj.toString()); // returns [object Object]
console.log(obj.nonExistentProperty); // returns undefined
try {
console.log(obj.nonExistingMethod());
} catch (e) {
console.log("Oops! " + e);
}
As you can see, somehow our obj
has a constructor
property and a toString
function. Just to prove they are the real deal, I invoked the nonExistingProperty
property (which returns undefined
) and invoked a nonExistingMethod
that simply blows up! Where did the toString
property come from? To answer that, add one more line to the script you ran in Listing 5, as shown in Listing 6
1
2
console.log(obj.__proto__);
Within Chrome, the final line should show look like the screenshot I have captured in Figure 1 (Unfortunately there isn’t a way to show the properties of the object within Firefox - All you will see is Object { ... }
)
Figure 1 - Object.prototype
What just happened here, and what is that Object
being shown in the console? Well, let’s take it a step at a time. obj.__proto__
is (in our example) equivalent to {}.__proto__
. Essentially, we are asking our object for its __proto__
object. You see, every object in JavaScript has a “prototype” - This “prototype”, is just that - an object that acts as the template for any new object. This is where the “prototypal” in “prototypal inheritance” comes from - rather than having classes, we simply “clone” existing objects to allow for behavior reuse and inheritance.
This is very different from the class based inheritance many of us are used to in languages like Java. Rather than have two completely different animals - classes and objects like you do in Java (Yes, although classes are objects at runtime you have to admit they have special powers :D), within JavaScript you have only objects. Objects point to other objects and share behavior!
The relationship between our obj
from Listing 5 and it’s __proto__
is illustrated in Figure 2
Figure 2 - {}.__proto__
Notice that I have marked our obj
’s __proto__
as Object.prototype
. The reason for this will become clear after we finish the section on Functions, but from now on, I will refer to it as just that - Object.prototype
.
Perhaps you see how method look up works in JavaScript now. When you asked for obj.toString()
, JavaScript looked in our newly created object to see if it had a toString
function. Needless to say, it didn’t find it, so then JavaScript follows the __proto__
pointer up to the Object
which does have a toString
! So JavaScript merely calls that function and returns. This is also the same reason why nonExistentProperty
and nonExistentMethod
both failed - neither our object nor it’s __proto__
have any such properties. Furthermore, Object.prototype
does not have a __proto__
property - so this is the end of the line for the lookup chain.
Now that you know this, can you think of a way to write a custom toString
method for our newly created obj
object? It’s not that much different from Java. See Listing 7
1
2
3
4
5
6
7
8
9
10
// declaring a new object because it's convenient
// you can use the code in Listing GAN-5 and Listing GAN-6 if you like
var obj = {};
obj.toString = function() {
return "I am a newly created object!";
}
console.log(obj.toString()); // evaluates to "I am a newly created object!"
What did we just do? We introduced a new property in our object, namely toString
. Now when we ask our obj
to evaluate toString
JavaScript does not have to go far - it merely executes the first found implementation of the function, which happens to be the one owned by our newly created object. To see how this works, see Figure 3
Figure 3 - instance toString implementation
Notice that I have merely stricken out the toString
in Object.prototype
and not removed it. By putting a newer definition in obj
you don’t wipe out the one in Object
, you merely shadow it. Furthermore, any new objects that are created without a toString
implementation still use the one in Object.prototype
. This is no different than inheritance you might be used to in Java-land. The top level (Java) Object
class offers a default implementation of toString
, and sub-classes can override it to suit their needs. The only difference being that in JavaScript there is no class
construct - rather you deal with objects only.
The discussion so far works out fabulously if you need only one object to work with - you can add as many custom methods on to this object as you need and be on your merry way. But classes (like those in Java), and inheritance in general buys us a lot more than that - the ability to have a way to define certain functions and properties for a class of objects in one place, and have all “instances” automatically be adorned with certain behavior.
What if we wanted all of our objects to be able to greet other objects? If we were to put a sayHello
method in Object.prototype
, then any object created with it’s __proto__
property pointing to Object.prototype
will automatically inherit that method. Let’s give it a whirl in Listing 8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// declaring a new object because it's convenient
// you can use the code in Listing GAN-5 if you like
var obj = {};
obj.__proto__.sayHello = function(name) {
return "Hello " + name + "!";
}
console.log(obj.sayHello("Brendan")); // evaluates to "Hello Brendan!"
// create another object
var anotherObj = {};
console.log(anotherObj.sayHello("Douglas")); // evaluates to "Hello Douglas!"
When we say obj.__proto__
we are getting a reference to the Object.prototype
object. We then tack on sayHello
on to it, and then create anotherObject
(which automatically inherits from Object.prototype
). This is illustrated in Figure 4
Figure GAN-4 - Global sayHello implementation visualized
Problem solved? Not quite. Most of us would be frowning right about now. By introducing the sayHello
in the Object.prototype
every object has the ability to speak! This includes the objects that JavaScript ships with. Try [].sayHello("John")
in your console. Needless to say, this is a sledge-hammer approach to inheritance. No Java programmer worth his salt would (if they could) put this method in Object
.
So what we need is a way to introduce newly created objects with their own “prototype” object - one that adorns them with specific behaviors that are applicable to any object of that class or genre. How do we do that?
That is the question we are here to answer. Before we go there, we need to talk at about functions in JavaScript^4 .
Functions
JavaScript is a functional programming language, in that functions are first-class citizens. This is a departure from languages like Java where functions, or rather methods are underlings to the objects to which they belong. What this means is that functions are like any other datatype in JavaScript - Just as you would assign a String
to a variable or return a Number
from a function (or a method), you can do the same with functions. You can assign a function to a variable (and you saw an example of this in Listing 2 where we assigned sayHello
to a function) and you can pass functions as arguments to other functions. You can even have functions create other functions a.k.a higher-order functions. Let’s explore some of these concepts really quickly in Listing 9
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// extremely contrived example but math operators in
// JS are baked into the language. This is just a simple
// wrapper
// This is NOT idiomatic.
function add(a, b) {
return a + b;
}
var somePointerToAdd = add; // have a variable point to the 'add' function
// You can invoke add in multiple ways
console.log(add(2, 3)); // returns 5
console.log(somePointerToAdd(2, 3)); // returns 5
console.log(somePointerToAdd == add); // prove they are the same - returns true
// This function creates an "incrementor" function and returns.
// Given an arg 'n' it returns a NEW function
// that takes just one argument and upon invocation returns 'add(n, arg)'
function incrementBy(n) {
return function(arg) { return add(n, arg) };
}
// essentially returns function(arg) { return add(3, arg); }
var incrementor = incrementBy(3);
// invoke function(arg) { return add(3, arg); } giving it 5 as an arg, returns 8
console.log(incrementor(5));
Note that incrementor
is a pointer to the newly created function that gets returned by incrementBy(3)
. This is no different than saying var x = "Brendan"
.
This is a powerful construct, and makes JavaScript an incredibly powerful language, but as is always the case with JavaScript, there is more to it than meets the eye. JavaScript functions, are in fact, full blown objects in and of themselves! If you were to glance at the JavaScript documentation you will see that there is a ‘Function’ object, it succinctly says
Every function in JavaScript is actually a Function object.
Our earlier discussion about objects, properties, and methods applies to functions as well. That is, functions can have properties and methods assigned to them! Whenever you declare a new function in JavaScript, a new Function
object is created, and much like other objects (like we saw in Listing 5) are blessed auto-magically with a few properties, of which, constructor
and prototype
being the ones relevant to our discussion at hand. You can get a handle to the function object simply by using it’s name, like Person
. (I noted in 2 that you could do "javascript".toUpperCase
, without the parentheses - that is essentially giving you back a handle to the toUpperCase
function). See Listing 10^5
1
2
3
4
5
6
7
function Person(name) {
// ignore the argument for this listing
}
console.log(Person.prototype); // evaluates to Person { }
console.log(typeof Person.prototype); // evaluates to "object"
Notice that the prototype
property evaluates to an object, which happens to be just another plain vanilla JavaScript object. See Figure 5 for a (simplistic) illustration on how this works.
Figure 5 - Function.prototype visualized
The object on the left is the Person
function object, and the object to the right is it’s “prototype”, which happens to be a plain JavaScript object. Two things of note here - Functions get a special “prototype” property (different from the __proto__
property we saw earlier), and the functions prototype object (which happens to be a simple JavaScript object) gets a __proto__
property just like we saw in the earlier section. The prototype object’s constructor
property points back to the function object itself. The way you refer to a function’s (in this case Person
) prototype
is just like you would any other property - <name_of_object>.<name_of_property>
- so to reference Person
’s prototype we say Person.prototype
.
If this looks familiar, it’s because it is. Earlier (in Figure 2) I marked our obj
’s __proto__
as Object.prototype
! Looking at Listing 10 what can we infer? Simply put, there is a function object called Object
, in that there is a function declared with the name Object (like so function Object() { ... }
), for which JavaScript creates a (Function
) object, and this object points to another (plain vanilla JavaScript) object that happens to be it’s “prototype”. See Figure 6 to get an idea of how this lays itself out.
Figure 6 - Object.prototype visualized
To summarize, functions in JavaScript are just objects, albeit a little special because they all have a prototype
property. This prototype
property is just another object, which like any other object that you create, is part of an inheritance chain with Object.prototype
at the end.
Now that we know all this, how can we use this to properly adorn our “person” objects with a predefined set of behaviors? Well, we still have one more hurdle to surmount before we get there, but unfortunately, we have reached the end of our journey for this installment. In part 2, we will first see how functions can be used as constructors, and use this to build our own inheritance hierarchies in JavaScript.
A minor digression
So far in this article, I have used the word “object”, and “instance”. This can be a contentious issue in JavaScript’s terminology due to the fact that JavaScript does not have the traditional notion of a “class” - so what exactly constitutes an “instance”? Well traditionally, in languages like Java, an instance is an object. But in JavaScript, as we have noted, constructs like the function Object
, and our own Person
(which as we will see in our next installment are the conduits to creating “instances”) are also objects.
One way to define an “instance” is like so - “instances” are created when we use the new
keyword along-with a function name. Consider var myString = new String("Douglas")
- in this case, myString
is an “instance” of String
. We should note that sometimes this is not so obvious. IN Listing 2 we used the literal syntax ({}
) for creating a new object - Under the covers this (roughly) translates to new Object()
- so once again, we have created an “instance” of Object
. Other examples include [1, 2, 3]
(which happens to be an Array
) instance, and /+d/
which is an instance of RegExp
.
The thing to take away here is that in a language like JavaScript, the distinction between “instances” and “objects” boils down to being borderline academic. As long as you understand how to use objects, and (in Part 2) how functions can be used as “instance factories”, in my opinion there is no reason to fret over the terminology.
Conclusion
JavaScript is inherently an object-oriented language. More so than many languages that we may have encountered, in that in JavaScript functions are also objects, albeit with a difference. Objects are adorned with properties, including a magical property called __proto__
, which allows the object to participate in an inheritance, and look-up chain. Functions, on the other hand, are a tad special, and have an additional property called prototype
and we will see how we can use this to build our own hierarchies in Part 2.
1. You can also use FireFox along with Firebug. Once you have that installed, create a new HTML file with the contents <!DOCTYPE html><title>On Protoypal Inheritance</title>
and save it anywhere on your computer. Then open the newly created file in Firefox, right-click anywhere on the page, and select “Inspect Element with Firebug”. This too has a menu bar of it’s own that has a “Console” option. Clicking that will bring you into the JavaScript console. If you are on a Mac, Safari (Version 5.1.3) also has a console similar to that of Chrome’s.
2. You could even do "javascript".toUpperCase
(note that there are no parenthesis following the method name) but this does something very different, and which hopefully will become more apparent as we go forward.
3. You can create the object, along with certain properties and methods in one fell swoop like we did in Listing 2 but I left that out for two reasons - It does not help our discussion here, and the syntax can be a tad confusing. For details see this link.
4. I realize that this section may have left you with more questions than answers, but I promise that it will all become clear soon.
5. I realize that the function has a name starting with a capital “P” vs. a lowercase as is usually the case. I can tell you that there’s a good reason :)