Javascript Prototype
Lets create an empty object:
> var A = new Object()
{ }
> A.prototype
undefined
What, A doesn’t have a prototype?! A is an object. Only functions have prototypes.
> var F = function() {}
> F
[Function]
> F.prototype
{ }
But wait, aren’t functions just objects in javascript? Well yes, but somewhat special objects. If you have a problem with that just refer to George Orwell’s definition from the Animal Farm: “All animals are equal but some animals are more equal than others.” Functions are special objects that can be used to instantiate other objects. Just add new before the function call and any function becomes a valid javascript class constructor.
> var Cake = function(name) {
..... this.name = name;
..... }
> var yellowCake = new Cake("yellow");
{ name: 'yellow' }
When you call a function using new, javascript creates a new empty object { } and passes it as this context to the function. We could have achieved the same without ever using the keyword new. Lets try that just out of spite.
> var cakeFromScratch = { }
> Cake.call(cakeFromScratch, "Look ma no new");
> cakeFromScratch
{ name: 'Look ma no new' }
We passed in empty object cakeFromScratch as this context using javascript call method, and Cake function has initialized its member name as expected. Now, we all know that good cakes should have a chocolate crust on top so lets add it to the prototype of the Cake function, then all cakes we already created will instantly get it.
> Cake.prototype = { crust : “chocolate” }
{ crust : “chocolate” }
> yellowCake.crust
undefined
Blasphemy!!! yellowCake has no crust! Isn’t it supposed to get it automagically? When we created yellowCake object, Cake.prototype was pointing to an empty object A. By executing Cake.prototype = {crust : “chocolate”} we have set the prototype to point to a new object B, while yellowCake instance is still pointing to A. In other words, we have changed to what Cake.prototype points to instead of updating it. This is what happened in memory across time:
Every object in javascript has a “secret” __proto__ field. When object is created using new, __proto__ is set to point to parents function prototype. What we should have done to make all cakes have crust is this:
> Cake.prototype.crust = “chocolate”;
Now we have changed the original prototype object. Don’t let javascript confuse you with it’s fancy names, prototype is just a member variable of a function object and is initially set to an empty object { }. You can change it entirely, add to it and remove from it.
> yellowCake.crust
'chocolate'
> cakeFromScratch.crust
undefined
Oh-oh, now cakeFromScratch has no crust! Well, since we manually initialized cakeFromScratch object, __proto__ was not set for us automatically. But we can fix that.
> cakeFromScratch.__proto__ = Cake.prototype
> cakeFromScratch.crust
'chocolate'
> cakeFromScratch instanceof Cake
true
Ta-da! We have manually created a legitimate cakeFromScratch without ever using new and as you can see, the instanceof operator agrees. The existence of __proto__ means that in javascript, we can easily change the type of object dynamically. Probably not the smartest thing to do but hey if God didn’t want us to mix cakes and donuts why did he give us both?
> var Doughnut = function() {}
> Doughnut.prototype.glaze = "vanilla"
> yellowCake.__proto__ = Doughnut.prototype
> yellowCake.glaze
'vanilla'
> yellowCake instanceof Doughnut
true
yellowCake is now a Doughnut. Strange but we can do it! The point of this exercise was not to demonstrate a clever pattern that should be used in real life, trust me, this one might be just a little too clever, but to demonstrate that the type of object is determined only by its __proto__ . It’s time to move on to inheritance. Not a problem, just make function.prototype point to an object you want to inherit from.
> var Dessert = function(calories) {
..... this.calories = calories;
..... }
> Dessert.prototype.yummy = "yes"
> Cake.prototype = new Dessert(100);
{ calories: 100 }
Cake now inherits from Dessert and it has same members that Dessert has. Members added to function prototype are shared fields. Fields added to this are member variables. Basically all desserts are yummy but they have different calories. That is, they should have different calories.
> var tiramisu = new Cake("tiramisu")
{yummy: 'yes', calories: 100}
> var cheeseCake = new Cake('cheese cake')
{yummy: 'yes', calories: 100}
Houston, we have a problem. Certainly both tiramisu and cheeseCake are yummy, but they should have different calories! When we created our objects, __proto__ field was set to whatever Cake.prototype was pointing to, which was an instance of Dessert object. A specific instance of Dessert object. In fact, an instance that has its calories count set to 100.
> tiramisu.__proto__
{ calories: 100 }
How do we initialize parent member variable calories in our constructor? We call the parent functions to do its part:
> var Cake = function(name, calories) {
..... Dessert.call(this, calories);
..... this.name = name;
..... }
> var tiramisu = new Cake("tiramisu", 300)
{ calories: 300, name: 'tiramisu' }
Excellent, tiramisu now has 300 calories. I know, I know… 100 was better. Don’t despair, the “calories = 100” member still exists!
> tiramisu.calories
300
> tiramisu.__proto__.calories
100
What we did in our constructor is create a field with the same name calories in the top level object, effectively masking the inherited calories in the __proto__ object. This is what prototype chaining is all about. When you “get” a field Javascript engine will dig down the __proto__ chain until it finds it or runs out of __proto__ objects. On the other hand setting a field is one lazy operation, it only operates on the top level object. If the field is there it will get updated, if not, it will be created. In the end, it all works out as expected.
Can we do better? Can we avoid having two calories fields?
The trick is to use Object.create instead of new. https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/create
> var baseObject = Object.create(Dessert.prototype)
> baseObject.yummy
'yes'
> baseObject.calories
undefined
Object.create() returned a new empty object with only its __proto__ set to Dessert.prototype. It never called the constructor. To summarize:
new Dessert(x)
1. creates new empty object A = {}
2. sets A.__proto__ to Dessert.prototype
3. calls Dessert function with object A as this context to initialize the member variables
4. returns A to the caller
Object.create(Dessert.prototype)
1. creates new empty object A = {}
2. sets A.__proto__ to Dessert.prototype
3. returns A to the caller
We can now use the baseObject for our inheritance chain to avoid having two calories fields
> Cake.prototype = baseObject
> var tiramisu = new Cake("tiramisu", 300)
> tiramisu
{ calories: 300, name: 'tiramisu' }
> tiramisu.__proto__.calories
undefined
Dojo has a nice browser-compatible trick for doing this:
// return a new object that inherits from ctor.prototype without running ctor on the object. function forceNew(ctor) { // create object with correct prototype using a do-nothing constructor xtor.prototype = ctor.prototype; // xtor is a global empty function var t = new xtor; xtor.prototype = null; // clean up return t; }
Just in case you’re wondering why do we even need a new object for our prototype hierarchy and why don’t we just do
> Cake.prototype = Dessert.prototype
Well because this wouldn’t be inheritance. Anything we add to Cake function prototype would now be visible in its parent Dessert class as well. We need a new object instance for our prototype so that we can add custom, Cake specific fields, without touching our parents prototype.
Multiple Inheritance with dojo.declare
First of, multiple inheritance is not really supported by the language, but there are ways around it, so to speak. In dojo, one can do the following to declare a Cake class, along with its parent classes
dojo.declare("Dessert", null, { yummy: "yes" }); dojo.declare("BirthdayItem", null, { event: "birthday" }); dojo.declare("Cake", [ Dessert, BirthdayItem ], { name: null });
How does this work? The truth is that only the first listed class is a true prototypical parent, while all the others are just “mixed in”. Dojo will create a new proto object for every mixed-in parent and then copy all the properties from the parent to the proto object. It will then chain these objects to create a custom prototype chain for your declared class.
Here is the relevant code from dojo, a little simplified to be more readable.
if (superclass) { for (i = mixins - 1; ; --i) { proto = forceNew(superclass); // forceNew from the previous section if (!i) { // stop if nothing to add (the last base) break; } // copy everything from base class prototype to proto object nextBase = bases[i]; // bases? - see below mixin(proto, nextBase.prototype); // create new empty constructor function to chain in our proto object ctor = newFunction; ctor.prototype = proto; superclass = ctor; } }
This code is a little complicated, so you should stare at it for a while. Ready? Only the first time through the loop we use the real superclass (Dessert in our case) to create the proto object. Every other time when we call forceNew() the superclass is a custom function object we created on the last three lines. Basically, we create the new proto object, fill it up, then make an empty function ctor that inherits from it, and finally use ctor to create the next proto object. In the end we have created the proto chain. This is what the final result looks like:
> Cake.prototype { declaredClass: "Cake", name: null, __proto__: { declaredClass: "BirthdayItem", event: "birthday", __proto__: { declaredClass: "Dessert", yummy: "yes", __proto__: Object // root javascript Object } } }
How does dojo decide what is the correct stacking order of the base classes and where does bases array come from? What if Dessert inherits from A then B, while BirthdayItem inherits from B then A? Which should come first in the Cake prototype chain: A or B? Well, it’s complicated, and you can read about it here http://www.python.org/getit/releases/2.3/mro/
Things to note:
1. Only the first base class in the list is a true prototypical parent.
If we change Dessert.prototype, Cake objects will reflect the change. However, cakes will be oblivious to changes that we make to BirthdayItem.prototype.
> Cake.prototype.__proto__ === BirthdayItem.prototype
false
> Cake.prototype.__proto__.__proto__ === Dessert.prototype
true
2. Fields are overridden from left to right.
If we had a field with the same name in multiple base classes, the field from the last listed class would win. This fact gave us a headache a few times. For example if Dessert had “color”: “blue” and BirthdayItem had “color”: “red”, our Cakes would be red.
3. Don’t use multiple inheritance
Java doesn’t even have it, C++ does but we all know how widely this feature is used in practice. Javascript doesn’t support it either and even though we can beat it into submission doesn’t mean we should. Instead, try to go back and think more about your class design.
4. Use mixins instead
Mixing in is copying fields from source object to destination object. Rather than using multiple inheritance and thinking about multiple parent objects, think about functions and design a batch of functionality. You can than mix it in wherever it’s needed: with prototype or with a specific object instance. Learn more about it here: mixins at twitter.
Rather than creating prototype chain, just extend your object with a mixin:
extend(Cake.prototype, BirthdayMixin); // mixin as an object - works by copying all members
or
BirthdayMixin.call(Cake.prototype); // mixin as a function - works by assigning to this
5. Think about performance
To chain in multiple base classes we have to create custom constructors and copy all the fields from base class to the new object. We can’t just point to base.prototype. We also have to decide the correct stacking order, and as I already mentioned, that step is a little complicated. Just look at that Method Resolution Order document, I’m not sure I want to understand everything that’s happening there. It might give me nightmares. On top of that, we can’t delay this work, it is done before we create instances of our class. This hurts first-load performance. Using mixins alleviates the problem and you can perform “mixing in” anytime you like. Maybe when the object is instantiated, maybe later. You gain grater flexibility and less headaches.