Just JavaScript - The Prototype Mechanism
Written by Ian Elliot   
Friday, 05 December 2014
Article Index
Just JavaScript - The Prototype Mechanism
Extended role of the constructor
Object factories and prototype

The prototype is about the most mysterious part of JavaScript. Once you have mastered the call context and the constructor, it is the prototype that you have to turn to. How does it work? How do you use it? What is it for?

Just JavaScript 

 There is a newer version of the draft of the book here.

A Radical Look At JavaScript

Contents

  1. JavaScript Isn't Java, or C, or C# ... (Book Only)
  2. In The Beginning Was The Object
  3. The Function Object
  4. How Functions Become Methods
  5. The Object Expression
  6. Object Construction
  7. The Prototype
  8. Type And Non-Type
  9. Constructor And InstanceOf
  10. Duck Testing And Prototype Construction

-Preface-

Most books on JavaScript either compare it to the better known class based languages such as Java or C++ and even go on to show you how to make it look like the one of these.

Just JavaScript is an experiment in telling JavaScript's story "just as it is" without trying to apologise for its lack of class or some other feature. The broad features of the story are very clear but some of the small details may need working out along the way - hence the use of the term "experiment". Read on, but don't assume that you are just reading an account of Java, C++ or C# translated to JavaScript - you need to think about things in a new way. 

Just JavaScript is a radical look at the language without apologies. 

JustJavaScripticon

 

 

The whole issue of the prototype object in JavaScript is a very contentious one - and don't expect to agree with everything I say in this introduction to the idea. What matters is that you understand completely what facilities are provided, why they were provided in the way that they are and have some ideas how best to use them.

The prototype object is a very strange idea and making sense of it depends on seeing the bigger picture.  The prototype object is intimately tied up with the idea and use of a constructor to create objects.

As we have already discovered in earlier chapters, there are other ways of creating objects than via a constructor and so this is not necessarily an essential part of JavaScript. If you want to you can ignore this whole issue and write programs without the prototype mechanism. You could, but you would be missing out on a very nice feature of the language.

The message is - don't make too much of the whole prototype idea.

The Prototype Delegation Principle

The actual prototype mechanism is very simple. 

Every object has an internal [[prototype]] property. It is internal in the sense that you cannot access it directly. There are non-standard ways of accessing it, and in ES6 there are standard ways, but in current JavaScript it is simpler to regard it as inaccessible. 

The question of how it gets set to reference another object is something that we will deal with later. For the moment what matters is that the [[prototype]] property is used to attempt to resolve any property references that the object does not support. 

That is, if you try to use a property that hasn't been defined on an object then the system looks for it on the object that [[prototype]] references. That is the [[prototype]] references an object that will be used to delegate any properties or methods that the original object doesn't have. 

This principle applies to all objects including any object referenced as a prototype. This means that if the prototype object doesn't have the property then its prototype object is searched and so on up the prototype chain.

The prototype chain ends when an object with a null [[prototype]] reference is encountered.

Many ways of creating an object set the [[prototype]] property to reference the object Object.prototype and this has its [[prototype]] property set to null so automatically ending the chain.

So what is the prototype chain for? 

The simple answer is that it provides a default set of behaviors for an object. If you set object A's prototype to be object B then, without any extra work, A automatically seems to have all of the properties of B.

Clearly if the same prototype object is used for a lot of other objects then this default behavior is obtained without having to duplicate all of the code. That is, one reason for using prototypes is that it is more efficient. 

Another reason is that if a set of objects all have a single prototype object then updating that object updates all of the objects that use it as a prototype. 

The temptation is to think of this as some sort of inheritance - but notice that this is not class-based inheritance and there is no suggestion that one object is a child or sub-object of another. There is no type hierarchy in JavaScript. You might say that object A "inherits" the properties of object B but it is more accurate to think of object A delegating any access to properties it doesn't directly support. 

Notice that this mechanism automatically shadows any prototype properties that the original object does implement. For example, if both A and B have property c then A.c accesses the property provided by A and B's property is shadowed or overridden by A's.

Properties that are directly provided by and object are known as its "own" properties. You can test to see if a property is directly provided using the hasOwnProperty function which is provided by the Object prototype. That is, any object that has Object in its prototype chain has access to a hasOwnProperty function - more of this later. 

Setting [[prototype]]

The idea of the prototype chain and the way it provides addtional properties for an object is fairly easy to understand. It is important that you keep this simplicity in sight while you get to grips with some of the messier details of how the prototype chain actually gets set up. 

The first thing to say is that pre-ES5 things were subtle and involved the new operator and the general idea of a constructor. This is still the version that most JavaScript programmers know today and it is important - but it is easier to start with the latest ES5 features. These are supported by all modern browsers. 

First two very simple and very basic settings of [[prototype]].

  • The Object built-in object has a prototype property that automatically references an object that provides default behavior for most JavaScript objects. For example, Object.prototype provides functions such as hasOwnProperty to other objects that have it in their prototype chain.
  • When you create an object literal its [[prototype]] is set to reference the same object that Object.prototype references. 

What this means is that all literal objects have the basic behaviour provided by Object.prototype. 

Now we come to the new ES5 facility.

The new Object.create(obj) method can be ued to create a new object with obj as its prototype - that is with its [[prototype]] property referencing obj.

Consider, for example:

var myProto={a:1,b:2,c:3};
var myObject= Object.create(myProto); console.log(myObject.a);

This first creates a literal object which is going to be used as a prototype for another object. Next Object.create is used to create a new empty object but with its [[prototype]] referencing myProto. Now when we try to use property a (or b or c) on the new object clearly it isn't found as an own property, but the search of the prototype chain leads directly to myProto and it does have property a which is used as if it was a property of myObject.

Notice that the prototype chain in this example is:

myObject->myProto->Object.prototype->null;

The literal object myProto has Object.prototype set as its prototype by default and Object.prototype has null as its prototype by default. 

The only problem with Object.create is that it is not suported by older browsers. However it is very easy to create a polyfill based on the original method of setting a prototype object - see later for more details.

Now we have a way of setting an object's prototype object we can explore some of its implicatons.

Object Prototype Interactions

The prototype object, and more generally the prototype chain, brings behavior to an object without you having to reimplement it. 

In the normal course of things a prototype object would be used more than once as a prototype. If it is only used once you might as well use the prototype as the main object as there is no advantage in delegating. 

For example:

var myObject1 = Object.create(myProto);
var myObject2 = Object.create(myProto);

Now myObject1 and myObject2 have the same prototype object and hence the same values of a, b and c. 

This raises the question of what happens if either of the objects tries to modify a property provided by the prototype?

The simple answer is that an object cannot modify a non-own property. For example, if you try:

var myObject1.a=10;

you will discover that myObject1.a is 10 and myObject2.a is still 1. When you try to store a value in a property the prototype chain is mostly ignored and an own property is created. From this moment on the own property is used and the prototype object's property is overridden. 

Note: There is a subtle twist to this in that if the prototype's property is set as read only then the assignment is disallowed and a prototype property is created. 

The object may not be able to modify the prototype object but any modifications to the prototype are immediately communicated to the object using it. 

For example:

var myObject1 = Object.create(myProto);
var myObject2 = Object.create(myProto);
myProto.a=10; 

results in both objects having a value of 10 for the a property - unless of course they have created an own property.

Using a prototype object to share data isn't often a good idea because as soon as the main object writes to a non-own property it is converted into an own property. This means you generally don't make any savings in storage when you supply data properties using a prototype.



Last Updated ( Sunday, 10 May 2015 )