Prototypes in JavaScript

JavaScript’s prototype system is often confusing for programmers coming to the language from an object-oriented background, as with C# or Java. Here I’ll try to dispel a bit of confusion by looking at how object prototypes work.

For what follows, it’s very useful if you enter the code into a console (such as can be found in the Google Chrome browser) and experiment with the various commands.

We can create an object by writing its fields literally, as in

var comic = {
  title: 'Spider-man',
  issue: 343
}

Type this into the console and then examine the comic object (by just typing ‘comic’ (without the quotes) on a line by itself and hitting return). Its type is given as Object, which isn’t surprising. Open up this node, and you’ll see that in addition to the two fields ‘issue’ and ‘title’, there is a third field called (in Chrome, though this won’t be true in all browsers) __proto__, whose type is given as Object. If you expand __proto__, you’ll see a list of fields that include the functions that are defined for all objects, such as toString, hasOwnProperty and so on.

The __proto__ object is the prototype that is attached to all objects by default. In its expansion, you may notice that it does not have its own __proto__ field. This is because the prototype of Object is the end of the prototype chain (more on this later). Thus it is more correct to say that all objects except Object’s prototype have their own prototype.

So what use is this prototype? Its importance arises from the fact that you can customize it by adding fields to it, or replacing it completely with an object of your own making.

We saw in the post on creating objects with a constructor that we can add methods to objects by adding a method to the constructor’s prototype. Let’s have a closer look at what’s going on here.

First, we need to make one thing clear. It is true that all objects (including functions) have the prototype we’ve been referring to by the name __proto__. However, functions (and only functions) also have a separate ‘prototype’ property, where in this case, the word ‘prototype’ is spelled out in full and not abbreviated or enclosed in underscores. Please understand that __proto__ and ‘prototype’ are not the same; only __proto__ is the true prototype of an object or a function. So what’s the ‘prototype’ property of a function used for?

When we use the ‘new’ operator to create a new object from a constructor, the new operator creates the new object and then links the object’s __proto__ to the constructor’s ‘prototype’. Note that this is a reference link from the object to the constructor, and not a copy of the constructor’s prototype to the object’s __proto__. This means that any changes to the constructor’s prototype are reflected in the __proto__ of all objects created from it. More on this below.

For example, suppose we define the Book constructor as in the previous post (we’ve made a slight change in that the name of the constructor has been moved to the function definition rather than being a separate var, but the effect is the same):

function Book(author, title, price) {
  this.author = author;
  this.title = title;
  this.price = price;
};

var book1 = new Book("Asimov", "Foundation", 3.55);

Now examine ‘book1’ in the console. You’ll find that it contains the three data fields defined in the constructor and also __proto__. Expand __proto__, and you’ll see that it contains the constructor, along with another __proto__, the latter of which is the base Object prototype. If you expand the constructor, you’ll see that it contains its own __proto__ and also a ‘prototype’ of type Book. It’s this ‘prototype’ that is used to create new objects.

If we add a method to the constructor’s ‘prototype’ like this:

Book.prototype.description = function () {
  return this.author + ': <i>' + this.title + '</i>  £' + this.price;
}

we can examine the __proto__ of ‘book1’ again. Now we’ll find that the ‘description’ function has been added:

> book1
  Book
    author: "Asimov"
    price: 3.55
    title: "Foundation"
    __proto__: Object
      constructor: function (author, title, price) {
      description: function () {
      __proto__: Object

If we expand ‘description’, we find that it, too, has a __proto__ and a ‘prototype’, since it’s a function. However, its ‘prototype’ isn’t used for anything, since ‘description’ isn’t a constructor.

A neat trick is to use prototypes to create a new object that inherits its prototype from an existing object. To do this, we can add a new method to Object (so that it’s available to all our code). This is the ‘beget’ method, and is due to Douglas Crockford:

if (typeof Object.beget !== 'function') {
  Object.beget = function (copy) {
    var F = function () { };
    F.prototype = copy;
    return new F();
  };
}

var book3 = Object.beget(book1);

The initial ‘if’ checks if ‘beget’ has already been defined (if it hasn’t, the typeof will return ‘undefined’). We then define Object.beget as a function which takes a single argument copy. Inside beget(), we create an empty function called F, and assign this function’s ‘prototype’ (not __proto__) to be the object copy that we want to copy. F is treated as a constructor, and the ‘new’ operator is used to create a new object from F, which is then returned. Recall that using ‘new’ creates a new object whose __proto__ is a copy of the constructor’s ‘prototype’, so the effect is to generate a new object whose __proto__ is copy.

The book3 object inherits all the fields of book1 and is shown as being of type F. Expanding F, we find that its only field is __proto__, which is of type Book, and if we expand that, we get the same fields as we had for book1.

If we now access book3.title, we’ll get ‘Spider-man’ which of course was copied from book1. What’s important to note, though, is that the ‘title’ field isn’t in the top layer of fields for book3; it’s one layer down, in the Book prototype. This illustrates that JavaScript will look for a field iteratively from the top down, so if it doesn’t find the field you want at the top layer, it will look at the next layer, and then the next, and so on, until it either finds the field or hits the Object.__proto__ at the bottom. If it doesn’t find it there, it will return ‘undefined’.

The prototype chain includes only the __proto__ fields; the ‘prototype’ properties have nothing to do with this process. They are used only in the creation of new objects in a constructor.

What happens if we try altering the data fields in these objects? For example, suppose we try writing book3.author = ‘Simak’. We find that the ‘author’ field of book3 is indeed now Simak, but has this assignment affected anything on the prototype chain? We can examine book3’s structure once again to find out.

We find that, rather than altering the ‘author’ field in the Book __proto__, book3 has a new author field at the top level. That is, we now have

> book3
 F
  author: "Simak"
  __proto__: Book
    author: "Asimov"
    price: 3.55
    title: "Foundation"
    __proto__: Book

Assigning a new value to an existing field in a __proto__ doesn’t overwrite the existing value; it adds a new value at the layer above the __proto__.  JavaScript ‘knows’ which author to use, since when we request the author field, it starts at the top in its search, so the first author found will be Simak. If we remove that field with the statement delete book3.author, we find that the top layer author is removed, and the author property reverts to that stored in the __proto__, so it becomes ‘Asimov’ again.

What happens if we do change a property in the __proto__? Suppose we start with book1 and book3 as above (without the episode where we introduced ‘Simak’), so both books have ‘Asimov’ as their author. We now try writing book3.__proto__.author = “Delaney”. Now we find that the authors of both book1 and book3 have changed to Delaney, since we changed the underlying __proto__ on which both these books are defined. (By the way, fiddling with __proto__ directly isn’t terribly good programming practice, not least because not all browsers support it. We did it here just to illustrate a point.)

We can modify a constructor’s ‘prototype’ after the constructor has been used to create objects and these modifications will be available to the existing objects. This is because the __proto__ in an object is a pointer back to the constructor’s prototype that was used to create it. For example, we could add a method to Book by writing Book.prototype.getPrice = function() { return this.price; }. Having done that, we can now write book1.getPrice() to retrieve book1’s price of 3.55.

[Beware of a gotcha here, though. Since __proto__ is a reference to the constructor’s ‘prototype’, if you redefine the constructor’s prototype to point to a totally new object, the __proto__ reference won’t follow this change; it will still point to the old object. That is, it’s OK to modify fields and methods of the ‘prototype’ object originally associated with the constructor, but it’s not OK to replace that ‘prototype’ object.]

Advertisements
Post a comment or leave a trackback: Trackback URL.

Trackbacks

  • […] should see entries for the 4 functions and also the ubiquitous __proto__ object we’ve discussed earlier, but there’s no sign of ‘balance’ or […]

  • By Inheritance in JavaScript « Programming tutorials on November 24, 2012 at 3:25 PM

    […] prototype is defined as ‘new this’, which means it gets a copy of X. Remember that the prototype of a constructor is what’s the __proto__ of an object created from that constructor points to, so new objects […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: