Tuesday, 13 September, 2016 UTC


Summary

Over the summer I gave a talk titled “The New Dragons of JavaScript”. The idea was to provide, like the cartographers of the Old World, a map of where the dragons and sea serpents live in the new JavaScript feature landscape. These mythological beasts have a tendency to introduce confusion or pain in software development.  
One area I covered were the quirks you might run into with JavaScript classes. Some introductions explain how classes work by describing the de-sugaring a transpiler applies to transform a class into the classical constructor function and prototype manipulation we’ve used in JavaScript for many years.
class Employee { constructor(name) { this._name = name; } doWork() { return `${this._name} is working`; } } // above code becomes ... let Employee = function(name) { this._name = name; }; Employee.prototype = { doWork: function() { return `${this._name} is working`; } } 
Constructor functions and prototypes are a useful mental model to have at times, but also leads to trouble because classes aren’t exactly like using constructor functions. For example, functions in JavaScript will hoist, but classes do not. If you ever want to push the definition of a small utility class to the bottom of a file and try to use the class in the code at the top of the file, you’ll be setting yourself up for an error.
// this code works const e = new Employee(); function Employee() { } // this code produces a ReferenceError const e = new Employee(); class Employee { } 
Technically, classes (and variables declared with let and const) do hoist themselves, but they hoist themselves into an area the early specs referred to as the “temporal dead zone”. Accessing a symbol in its TDZ creates a RefernceError.  As an aside, “Temporal dead zone” is, I think, one of the greatest computer science terms ever conceived and should also be the title of a Hollywood film starring Mark Wahlberg.
Another difference between creating an object using a class and creating an object with a constructor function is in reflective code. It’s easy to discover the methods of an object instantiated with a constructor function using a for in loop.
const Human = function () { } Human.prototype.doWork = function () { }; let names = []; for (const p in new Human()) { names.push(p); } // ["doWork"] 
The same code won’t work when using a class definition.
class Horse { constructor() {} doWork() { } } names = []; for (const p in new Horse()) { names.push(p); } // [] 
However, it is possible to get to the methods of a class using some Object APIs.
names = []; const prototype = Object.getPrototypeOf(new Horse()); for(const name of Object.getOwnPropertyNames(prototype)) { names.push(name); } // ["constructor", "doWork"] 
Coming soon – The Troubles with Modules.
Previously in this series – The Trouble with JavaScript Arrow Functions