JavaScript Prototypes for the Confused

A tutorial on JavaScript prototype-based inheritance.

Objects can have a prototype and a prototype is just another object. We can get an object’s prototype using the Object.getPrototypeOf method.

Object.getPrototypeOf({}); // → {}

Yes, JavaScript really loves objects. It’s objects all the way down. Actually that’s not completely true:

Object.getPrototypeOf(Object.getPrototypeOf({})); // → null

The first call to Object.getPrototypeOf gave us the prototypical object—the one that acts as the prototype for practically all objects—and calling Object.getPrototypeOf on that gives us null. You’ve got to stop somewhere, right?

Things like arrays and strings are objects too so they also have prototypes:

Object.getPrototypeOf(""); // → [String: '']
Object.getPrototypeOf([]); // → []

As you can see they have a different prototype to vanilla objects, but this is only in the immediate sense:

Object.getPrototypeOf(Object.getPrototypeOf("")); // → {}
Object.getPrototypeOf(Object.getPrototypeOf([])); // → {}

Prototypes can be seen as ancestors: an object being the descendent of it’s prototype. In the previous case an array is a descendent of the array prototype which is a descendent of the object prototype etc.

Why?

An object shares the properties of it’s prototype so it’s a convenient way of sharing data and code between a set of similair objects.

let proto = {
  x: 1
};

let foo = {
  y: 2
};

Object.setPrototypeOf(foo, proto);
console.log(foo.x); // → 1

Constructors

The canonical way of constructing an object based on a prototype is by defining a function. These types of functions are called constructors. There’s nothing particularly special about the functions themselves—but rather how they’re used. Rather than calling them like a normal function, we use the new keyword. This binds uses of the this keyword in the body of the function to a newly created object.

function Constructor() {
  console.log(Object.keys(this).length);
}

// `this` is bound to the global object
Constructor(); // → 22
// `this` is bound to a newly created object
new Constructor(); // → 0

All user-defined functions have a prototype property.

function foo() {}
console.log(foo.prototype); // → foo {}

This property contains an object unique to this function.

All new objects created using this function (and the new keyword), have this unique object set as their prototype.

function Constructor() {}
let foo = new Constructor();
console.log(Constructor.prototype === Object.getPrototypeOf(foo)); // → true

Any changes to this prototype object will be reflected in all objects created using it.

function Constructor() {}

let foo = new Constructor();
let bar = new Constructor();

Constructor.prototype.moo = () => console.log("Moo.");

foo.moo(); // → Moo.
bar.moo(); // → Moo.

I hope this article has left you less confused than when you started reading it.

Love and kisses,

Simon