Illumination on JavaScript Prototypes
You don't have JavaScript enabled. If you wish to run the JavaScript example code, you will need to enable JavaScript and refresh the page.
I saw a video the other day about how to implement classes in JavaScript. I thought the video was awesome, but it left me feeling that if you didn't already understand what he was talking about, you wouldn't have understood what he was talking about. So I have written this article to demonstrate how prototypes can be used to emulate class inheritance in JavaScript. I didn't keep the link, but I think it is one of Douglas Crockford's JavaScript videos.
One of the pages of Cassianus Corvina
A Hungarian illuminated manuscript from 1498 (Illumination on parchment)
Prototypes Are Easy
Prototypes are used to simulate class heirachies in JavaScript*. So if you want to use classes in JavaScript, you need to understand how prototypes work.
Fortunately once you understand a few key points, prototypes in JavaScript do actually make sense!
I found that when I first looked at examples of using JavaScript prototypes, the code just didn't make sense to me. It was a long period of time I finally worked out my fundamental misunderstandings. So in this article I have tried to explain prototypes clearly, with the goal of helping others from having the same misunderstandings.
* Although prototypes may have other uses, they are mostly used for implementing classes.
Prerequisites
There are a few things about JavaScript it helps if you know before getting started:
- You should have a working knowledge of JavaScript syntax.
- You should understand that objects in JavaScript are like objects in other scripting languages, and not at all like objects in a strongly typed language like C or C#. An object in JavaScript is similar to a hash or a dictionary - you can add extra properties to an object at any time.
- I would help if you understand how in JavaScript methods on an object are actually just normal properties - properties that contain a reference to a function.
- It would help if you understand that in JavaScript functions can be used like objects (e.g. setting properties on a function).
However, I have tried to write the article so that the above concepts are also explained where relevant, because sometimes the concepts are not obvious or well known to to many programmers; especially if you have only had experience of normal imperative languages and who are new to JavaScript.
Prototype Concept
Lets say we want to implement the above classes. JavaScript has no inbuilt* concept of classes. To implement this we create a single object for each class. For example, if three leaf objects were created, you would have the following objects:
* the latest versions of JavaScript do support classes directly, but if you want your scripts to work cross-browser with installed browsers, then you can't use the latest syntax.
In the diagram above, each arrow represents a hidden internal reference from each object to its prototypical parent object. All JavaScript objects have a hidden internal property that links upwards to the object's prototype. This reference is called the object's prototype.
The functioning of the prototype is straightforward: whenever code accesses a property on an object, if the property
does not exist on the object, then the property is looked for on the prototype. The search for the property
continues up the chain of prototypes until the property is found. If it is not found then undefined
is returned as the value.
Prototype Demonstration
some properties with values, and then have a play with the following to demonstrate how JavaScript prototypes work:
You don't have JavaScript enabled. None of these examples will work.
| property | ||
| value to | ||
| function | ||
| property | ||
| value to | ||
| function | ||
| property | ||
| value to | ||
| function | ||
| property | ||
| value to | ||
| function | ||
| property | ||
| function | ||
| property | ||
| value to | ||
| function | ||
| property | ||
| function | ||
| property | ||
| value to | ||
| function | ||
| property | ||
| function | ||
The above example is just for properties, but the above so that you can add and call functions too, and you can see how it works for functions. You might want to refresh the page first if you created lots of properties!
I hope the above demonstrates how prototypes actually work. Fortunately the actual concept of prototypes is pretty easy to understand (even just by reading other articles about prototypes - even if the specifics of how they are implemented is not so clear!).
Prototype Madness
Background: Functions are Objects
In JavaScript a function is actually just an object with special behaviour if used as a call - the () operator. This may be hard to understand if you are used to a normal imperative language. For example (nothing to do with prototypes):
The following is the same as the above (for someFunction1), but it uses a variable set to an anonymous function.
It sets the variable someVar2 to be a reference to the anonymous function function() { alert('You called 2?'); } declared after the = sign.
It is an anonymous function because it doesn't have a name (someVar2 is a variable, not the name of the function).
In some languages (and in academic articles) anonymous functions are called lambdas.
The Prototype Reference is Hidden
Every JavaScript object has a hidden prototype reference.
What seems insane is that you can't find out what the prototype reference is [I hope that this statement is wrong - if right then that's braindead], and you can't directly initialise it, and you can never change it.
Some JavaScript implementations allow you to read the prototype using a property called __proto__.
Setting the Prototype Reference
OK, this part is the spooky part. The following code makes a newDescendent object that has a prototype of rootProto:
Note that there is nothing special about the prototype property set on a constructor function - it is just like any other property which you may set on a normal function object.
Constructor Functions
It is critical to realise that the only way you can set the hidden prototype reference in JavaScript is to use the new operator.
A constructor function is a normal function that you have called using the new operator.
Usually the function will have a prototype property set on it.
Only when you use the new keyword in conjunction with the function does the prototype
property get used in a special way. The prototype property of the
function gets used by new to initialise the hidden prototype reference
on the new object. So the prototype property should be set to the
parent object you want your new objects to use for their prototype
link. For example:
The above also shows that a constructor function is much the same as a constructor function in many other languages.
Below demonstrates that you may have multiple constructor functions that use the same prototype — constructorFn9 is using the same prototype as constructorFn8.
Dynamic Prototype Modification
The objects you use for your prototypes
are just normal objects. If you modify them then the changes are
immediately visible in any other objects that were constructed with
that prototype object. So using the prototype object baseProto by calling the constructor constructorFn9 from the previous example:
Classes and Subclasses
The above example code only shows how a single class is implemented. Simulating a class heirachy is exactly the same - however it looks complex because of the hoops you have to go through to get the hidden prototype references set correctly.
An alternative scheme shown below uses the constructor functions to initialize the prototype objects. This style is used by many people and has much the same result (although there are subtle differences).
Class Emulation Lacks Normal Features
Using prototypes to emulate classes is
not perfect and some normal things you expect classes to do are missing
or odd, because they cannot be emulated easily. Most notable it is
difficult to implement method overriding.
In other languages you use the base or super or inherited keyword (amongst others) to call an overridden method.
It is complex problem to try and solve in JavaScript and every solution has its caveats, so I am not going to discuss it.
There are plenty of different solutions you can find with different compromises.
Another oddity is that emulated classes in JavaScript are actually made up of multiple objects, and the same property can exist on the different objects. This is hard to explain, but it is clear if you use the Prototype demonstration above and set up the same property at different levels in the heirachy.
Breviarium
National Sz'ch'nyi Library, Budapest (1480s, Tempera and gold on parchment, 302 x 225 mm)
Summary
Main points:
- All JavaScript objects have a hidden reference to their parent prototype object.
- The only way to set the hidden prototype reference is to use the new keyword in conjuntion with a constructor function.
- A constructor function is just a normal function, with a property called prototype set to the parent prototype object you want to use.
Some Relevant Links
- Wikipedia: Prototype-based programming
- Object Hierarchy and Inheritance in JavaScript
- Joshua Gertzen: Object Oriented Super Class Method Calling with JavaScript
- Dean Edwards: A Base Class for JavaScript Inheritance
Thank You
The article hasn't been up for long so any comments would be welcome.
I am happy for you to email me, or you can leave a comment below (although note that I don't check comments particularly often).
I especially want to know about any mistakes.
If you have any significant improvements then send me your edits to the HTML page!
When the changes are posted up, I'll give you some cred (get your name on teh intarweb!) and I am happy to share some link love.
Thanks for reading!
Morris Johns
Appendix: A Variety of Remarks
Because there is no standard way to implement classes, every library, and every article, implements classes in their own quirky manner. This makes it difficult to understand other people's code. This section contains a few random notes about some code you may see in the wild.
Every function has a default prototype property that is an empty Object. The above code adds a getName property to that prototype (the second line above doesn't need to initialise the BaseClass.prototype property).
It is often useful to keep a reference to the prototype used to construct an object. In JavaScript prototype is not a reserved word so some people use a property called prototype to hold that reference (see last line in above).
Many people set up functions within the constructor function. The properties set up this way are not shared between objects created using the BaseClass constructor. The above code shows two alerts, "**func1**" followed by "**FUNC1**". However if you use one of the new objects as the prototype for a different constructor then they will be shared by descendents.
Code like the line BaseClass.prototype.func1.apply(this, arguments); is often used to call a super/base function.
The above code shows an alert "**DerivedClass.func1**" followed by "**BaseClass.func1**".
You cannot just use BaseClass.prototype.func1() because then in BaseClass.func1 this would be set to the BaseClass.prototype object (this is quirky in JavaScript if you are used to traditional OO languages).
Note that if you are unfamiliar with arguments, it is similar to @_ in perl or ... in C. arguments
is an array of all parameters passed in with the function call, even if
no parameters were declared by the function, or if there were less
parameters declared than were passed in.
The above adds to the previous example and it demonstrates the technique used Dean Edward's base library to use this.base(arguments);
to call a super method.
Basically it adds a wrapper function around every function.
Of course Dean's code is a bit smarter than that (it only adds the
wrapper if the function contains the word base) also it looks like he
has put it into a more comprehensive library called base2.
Douglas Crockford has yet another technique but I can't recommend looking at the code.
ECMA Appendix
A few snippets from the ECMA-262 ECMAScript Language Specification.
8.6.2 Internal Properties and Methods
Native ECMAScript objects have an internal property called [[Prototype]]. The value of this property is either null or an object and is used for implementing inheritance. Properties of the [[Prototype]] object are visible as properties of the child object for the purposes of get access, but not for put access.
4.2.1 Objects
ECMAScript does not contain proper classes such as those in C++, Smalltalk, or Java, but rather, supports constructors which create objects by executing code that allocates storage for the objects and initialises all or part of them by assigning initial values to their properties. All constructors are objects, but not all objects are constructors. Each constructor has a Prototype property that is used to implement prototype-based inheritance and shared properties. Objects are created by using constructors in new expressions; for example, new String("A String") creates a new String object. Invoking a constructor without using new has consequences that depend on the constructor. For example, String("A String") produces a primitive string, not an object.
ECMAScript supports prototype-based inheritance. Every constructor has an associated prototype, and every object created by that constructor has an implicit reference to the prototype (called the object's prototype) associated with its constructor. Furthermore, a prototype may have a non-null implicit reference to its prototype, and so on; this is called the prototype chain. When a reference is made to a property in an object, that reference is to the property of that name in the first object in the prototype chain that contains a property of that name. In other words, first the object mentioned directly is examined for such a property; if that object contains the named property, that is the property to which the reference refers; if that object does not contain the named property, the prototype for that object is examined next; and so on.
In a class-based object-oriented language, in general, state is carried by instances, methods are carried by classes, and inheritance is only of structure and behaviour. In ECMAScript, the state and methods are carried by objects, and structure, behaviour, and state are all inherited. All objects that do not directly contain a particular property that their prototype contains share that property and its value. The following diagram illustrates this:
CF is a constructor (and also an object). Five objects have been created by using new expressions: cf1, cf2, cf3, cf4, and cf5. Each of these objects contains properties named q1 and q2. The dashed lines represent the implicit prototype relationship; so, for example, cf3's prototype is CFp. The constructor, CF, has two properties itself, named P1 and P2, which are not visible to CFp, cf1, cf2, cf3, cf4, or cf5. The property named CFP1 in CFp is shared by cf1, cf2, cf3, cf4, and cf5 (but not by CF), as are any properties found in CFp's implicit prototype chain that are not named q1, q2, or CFP1. Notice that there is no implicit prototype link between CF and CFp.
Unlike class-based object languages, properties can be added to objects dynamically by assigning values to them. That is, constructors are not required to name or assign values to all or any of the constructed object's properties. In the above diagram, one could add a new shared property for cf1, cf2, cf3, cf4, and cf5 by assigning a new value to the property in CFp.
