JavaScript: Objects, Prototypes and Inheritance

May 9th, 2013

JavaScript is a prototype-based object-oriented programming language. The prototype-based programming paradigm characterizes itself by the absence of classes. Reuse of behavior is performed by cloning existing objects, known as prototypes.

Examples of other prototype-based programming languages include Self and Io, and of course other ECMAScript implementations. [As JavaScript is an implementation of ECMAScript most things in this article are true for other ECMAScript implementations too.]

Because there are no classes you get an object by simply creating one. For example, the following creates an object that has a single method.

var bart = {
  introduce: function() {
    return 'Hi, Bart here';
  }
}

This allows me to introduce myself:

bart.introduce()
> 'Hi, Bart here'

Of course this is nice and all but not of too much help. If I want to create yet another person who can introduce herself I have to duplicate the introduce function. Like class-based programming languages group behavior in classes, we want to group the behavior of a person in an object prototype.

The prototype would look something like this:

var Person = {
  introduce: function() {
    return 'Hi, ' + this.name + ' here';
  }
}

To reuse the behavior of this prototype in JavaScript we must manually set its __proto__. [This is not recommended and I’ll get to that in a second.]

var bart = {
  name: 'Bart'
}
bart.__proto__ = Person
bart.introduce()
> 'Hi, Bart here'

Like I said, this is not recommended. I included this example because I want to show that there’s not much magic going on, it’s just all about the objects. The reason for this not being recommended is that __proto__ is the internal prototype used for implementing JavaScript’s inheritance.

In fact, __proto__ had the value of Object before manually changing it to Person. [This is not exactly true, but consider it the truth for now. I’ll reveal the lie towards the end of this article.] JavaScript tracks the internal prototype on object construction.

To get JavaScript to automatically track the prototype, the Person object must be rewritten as a constructor object.

function Person() {}
Person.prototype.introduce = function() {
  return 'Hi, ' + this.name + ' here';
}
var bart = new Person()
bart.name = 'Bart'
bart.introduce()

[Normally I would pass in name as a constructor argument on Person, but that discussion is beyond the scope of how objects work in JavaScript.]

Beside defining Person as a function now, something else changed here. Something important. The introduce function is no longer an attribute of Person but instead of its prototype. This is necessary because when copying from an object, JavaScript limits access to the object’s prototype, not to its own attributes.

The way to copy from objects is by using keyword new on a function. This implies that you can only copy from objects that are actually a constructor and thus objects that have Function in their prototype-chain (and most of the time are direct descendants of it). Because Function is an object itself you can use it like any other object and put attributes on it. But these attributes are put on the constructor and are not inherited by descendant objects. Copies only get access to the prototype. That is due to how JavaScript manages the object’s internal prototype. In the above example, the following is true.

bart.__proto__ === Person.prototype // => true

This might seem a bit strange, but that is only because it is a bit strange. JavaScript’s behavior for copying objects is different from other prototype-based programming languages (like Self and Io). In Self an Io objects have constructors themselves and you can copy or clone (respectively) an object.

I started this article by showing how to manually set up the prototype-chain for an object and that there’s not much special to it. While JavaScript’s management of the internal prototype-chain may seem much like the work of a sorcerer, it’s basically all objects (and some internal state).

In the beginning I also told you a lie. It should be clear by now that the internal prototype of the object was not Object but rather Object.prototype. This is also where the prototype-chain ends.

Explaining JavaScript objects this detailed helped me to really understand how it works. I hope that writing this down helps you getting a better understanding of JavaScript objects, too.