Prototypes in JavaScript

JavaScript has prototypes, and prototypes are weird. So weird, in fact, that some languages (like CoffeeScript) that compile down to JavaScript try to paper over this fact in an attempt to present a more wholesome, easily digestible package. However, once you learn to use prototypes they can be an amazingly useful tool in your arsenal.

Classless Society

So you’re in your studio, making the latest in your hits series of games, “Call of Murder.” You’ve already made like eight of these, so you decide to change it up and code the whole thing in JavaScript. Easy enough, right? However there’s this little problem – JavaScript said it was object-oriented, but you can’t figure out how to define a class. As it turns out, you can’t use classes! At least not in the traditional sense. But there’s definitely objects.

Your First Object

soldier = new Object()

The previous code creates an object and calls it soldier. What do you want the soldier to do? How about jumping? You can map the A button to a jump() function that you’ve defined elsewhere, as shown below.

soldier.a = jump

You’re essentially setting the a property on the soldier to be equal to a function. Notice that we didn’t include parentheses. That’s because when you include parentheses with a function in JavaScript, it calls that function. If you don’t include the parentheses, it just returns the a reference to the function itself. So, if you call soldier.a(), it will execute the function. But, if you write soldier.a without the parentheses, then it will return the actual function.

Also, take note that you are assigning a function directly on an object. What we’ve noted so far are quirks of JavaScript, but not directly related to the prototype system. So, given that you’re fairly experienced with scripting in JavaScript you move on and assign functions to the other buttons, as shown below.

soldier.b = punch
soldier.x = reload
soldier.r = machineGun

Copy-Paste Hell

Now, you want to create a second player who is almost exactly like the first one, except that they use a sniper rifle instead of a machine gun. There are two ways of doing this. The first is to repeat the code, as shown below.

sniper = new Object()
sniper.a = jump
sniper.b = punch
sniper.x = reload
sniper.r = snipe

It works, but it’s tedious. Especially since you promised the game designer that he could put 100 different types of characters in the game, each with different combinations of the same 200 moves. That’s a lot of repeated code, a lot of copy-paste, and a lot of potential bugs.

Rescued by Prototypes

So, you recode your sniper using an object() function, as shown below. We’ll come back to the actual implementation of object().

sniper = object(soldier)
sniper.r = snipe

You load up your game just to make sure, and start playing as a sniper. Sure enough, the A button makes your character jump. You press the X button, and it reloads. You press the R button, and it shoots the sniper rifle. It appears that the sniper has inherited all the traits from soldier, then overwritten the function mapped to R. If you were explaining it in English, you would say “a sniper is like a soldier, except it shoots a sniper rifle.” Notice the similarity of the English sentence to the code defining the sniper.

Function Theft

Let’s implement a wounded sniper. They’re like a normal sniper, except when they try to jump they cry out in pain.

woundedSniper = object(sniper)
woundedSniper.a = function(){console.log('aaaargh my leg!')}

Here, we used an anonymous function instead of a previously named function. That’s fine, but it means we’ll have to do something a bit different when we’re defining the woundedSoldier class.

woundedSoldier = object(soldier)
woundedSoldier.a = woundedSniper.a

Did you see that? You stole a function right off of another object! Try doing that in a regular object-oriented language. I dare you! (please note: this is an actual dare. I would not be surprised if someone accomplished it, but I would be surprised if it was as easy or as beautiful as this.) Now it’s time to look at the object() function.

Behind the Curtain

function object(o){
  function F(){}
  F.prototype = o;
  return new F();
}

object() takes in one argument (o, or what we might traditionally refer to as the parent object). It creates a dummy “class” function F(), complete with an empty constructor. It sets the prototype property of the class to the argument you passed in. Then, it returns an instance of the dummy class. Douglas Crockford has officially endorsed the object(o) method over the class system. In fact, that’s where we got the function.

The prototype Property

The one lingering question you’re left with is, what is that prototype property. Maybe it could be useful in your own code? If you print F.prototype to the console when creating a sniper, you get the following output:

{ a: [Function], b: [Function], x: [Function], r: [Function] }

You’ll recognize these as the mappings we made from the various buttons to functions. This is to be expected, because we’ve assigned the soldier object to the prototype of the dummy class F. However, we then create an instance of the dummy class and assign it to the sniper. If you ask what the prototype of sniper is, you get undefined. What happened? And how does sniper know what to do when you hit the X button?

Even more weirdness – when you are creating woundedSniper, the prototype of F only returns a mapping to the R button. But, clearly the woundedSniper can reload and punch. How do we explain all this?

__proto__

Confusingly, there are two prototype properties. When the constructor is called, prototype is assigned to the __proto__ property. sniper.__proto__ returns the following:

{ a: [Function], b: [Function], x: [Function], r: [Function] }

Even more interestingly, sniper.__proto__ has a __proto__ of its own. sniper.__proto__.__proto__ gives you the functions that soldier inherited from Object. Meanwhile, prototype is simply used in the constructor to set the __proto__ property on the actual object.

Conclusion

With our use of the object(o) function and our discovery of __proto__, we see that prototypical code can do as much as classical object-oriented code. The ability to define unique instances on the fly, as well as the ability to transfer a function directly from one instance to another at any time, make it ultimately more powerful.

Further Reading

This article was partially inspired by Steve Yegge’s blog post/book. Read that article, and keep an eye out for an upcoming post on how CoffeeScript creates a traditional class system out of JavaScript prototypes.

Win an Annual Membership to Learnable,

SitePoint's Learning Platform

  • Phong Thai

    before jQuery and some others become popular, prototype is most used in js

  • Jeffrey Biles

    To be clear, I’m not talking about the javascript framework called ‘prototype’. I’m talking about the language feature.