Prototypes in the pub, with help from the Terminator

var terminator = { phrase: ‘Hasta la Vista Baby!’ };

Two of my favourite things about working as a developer at FOODit are:

  • a learning budget to attend conferences etc.
  • beer on Friday afternoon.

Last week I took both to their logical conclusion and attended Halfstack 2016, an annual JavaScript conference in a pub. It’s a whole of day of short, engaging talks about various aspects of JS.

As I’m still in my first year of coding, the talk that stood out the most was ‘Discussing Prototypes with the Terminator’, which explained some really fundamental concepts in an engaging way.

With a bit of help from Arnold Schwarzenegger (in the form of classic Arnie movie clips), Christoph Gockel, the software craftsman behind the talk, took us back to basics by discussing the often-misunderstood JavaScript prototype and how it differs from classes used in other languages. Here I’m going to briefly explain the difference between the two:

JavaScript prototypes

An object is just a container of properties and functions, for example:

var terminator = {
model: ‘T-800’,
catchphrase: ‘Hasta la vista baby’
};

But let’s make things slightly more complicated and make a Terminator constructor function

function Terminator() {
this.model = ‘T-800’;
this.phrase = ‘Hasta la Vista Baby!’;
this.saySomething = function() {
console.log(this.phrase);
};
this.manufacturer = ‘Skynet’;
}

When called with the keyword new a constructor function will return a new instance of Terminator. The properties defined on the this of the function will be the properties of the generated object:

var arnie = new Terminator();
arnie.saySomething(); // => ‘Hasta la Vista Baby!’
arnie.model; // => ‘T-800’

The properties specified in the constructor are called the objects own properties. But in JavaScript, every object also has a prototype. In this case, because we haven’t specified the prototype, the arnie object will inherit the Object prototype, which is built into JavaScript and which is like the grandparent of all prototypes.

What does this mean in practice? Well if we call arnie.toString, we get:

arnie.toString; => [Function: toString]

So there is a function defined on the arnie object that we didn’t specify anywhere. What’s going on?

When you ask for a property name or function on an object, JavaScript will first look on the objects ‘own’ properties. If it can’t find anything it will then check the prototype. If it finds something that matches there it will return the result just like a normal call to an object.

This is why we can call toString() on arnie and get something back even though we didn’t define it ourselves.

We can use prototypes to set up inheritance between objects ourselves. For example, let’s say we want a new constructor function for the next generation of Terminators, the T1000 model:

function T1000(phrase) {
this.model = ‘T-1000’;
this.phrase = phrase;
this.material = ‘memetic polyalloy’;
this.specialAbilities = [‘shapeshifting’];
}
T1000.prototype = new Terminator();

Setting the prototype property of the constructor to be an instance of Terminator sets that as the prototype for all instances of T1000

var t1001 = new T1000(‘I like your gun’);
Object.getPrototypeOf(t1001);
// => {
// model: ‘T-850’,
// phrase: ‘Hasta la Vista Baby!’,
// saySomething: [Function],
// manufacturer: ‘Skynet’
// }
// - makes sense as we set the prototype to be an instance of Terminator
console.log(t1001.model); // => ‘T-1000’ — specified in the T1000 constructor and so overrides the prototype
console.log(t1001.manufacturer); // => ‘Skynet’ — comes from the prototype
t1001.saySomething(); // => ‘I like your gun’ - phrase passed to 
// T1000 constructor overrides phrase on the prototype. The function // is defined on the prototype.

For years this has been the way to do object-orientated programming (OOP) in JavaScript. Well in fact it still is, but since ES2015 (aka ES6), we have syntactic sugar added to JavaScript that allows developers to use classes, even though what is really happening under the hood is prototypal inheritance…

Halfstack 2016 (photo: https://twitter.com/mnemonicCloud)

ES6 Classes

To get inheritance using the ES6 class syntax, we do:

“use strict”;
class Terminator {
constructor() {
this.model = ‘T-800’;
this.phrase = ‘Hasta la vista baby’;
this.manufacturer = ‘Skynet’;
}
saySomething() {
console.log(this.phrase);
}
}
class T1000 extends Terminator {
constructor(phrase) {
super();
this.phrase = phrase;
this.model = ‘T1000’;
this.material = ‘memetic polyalloy’;
this.specialAbilities = [‘shapeshifting’];
}
}
var t1001 = new T1000(‘Where is John Connor?’);
console.log(t1001.manufacturer); // => ‘Skynet’
console.log(t1001.saySomething()); // => ‘Where is John Connor?’

Christophe went into detail comparing the two approaches and how each can be used in OOP. One of his points was that while the class syntax is helpful for developers transitioning to JS from class-based languages, we should know about the prototype, which is a totally valid way of achieving object inheritance.

At FOODit we use both JavaScript and TypeScript, which has classes but compiles into JavaScript. This means that Christophe’s talk definitely applies to me as I encounter code written in both ways on a daily basis. I don’t have a preference for either on the basis of familiarity alone (I’m a relative newcomer to both) but If I had to choose one or the other I guess I find class syntax a bit easier and less confusing to work with. It’s just less verbose and I’m less likely to get caught out by eg. prototype properties being defined ‘on the fly’. However I’ve really benefitted from Christophe’s talk and having to explain the prototype in this post.

I can definitely recommend Halfstack (the organisers hold regular meetups in London so you don’t need to wait a year). And if you get a chance to hear Christophe’s Terminator talk somewhere then make sure you check it out.

Useful links

Like what you read? Give Sam Rae a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.