Hoisting in JavaScript

Two weeks ago I gave a lightning talk at Caphyon on hoisting in JavaScript. Putting aside all the common jokes about the JS language, people really seemed to like it. It was kind of a challenge to talk about JS while having an audience of C++ and Java colleagues.

Now getting back to the talk, that was lightning ⚡️ fast and it didn’t covered that much as I would’ve liked to. So I’ll try to write a bit more on JavaScript scoping and hoisting bellow.

JS the good parts vs the definitive guide

Hosting Hoisting

On the clickbait talk title, that’s actually a true story. Last time I mentioned the hoisting term into an article of mine, I remember that a friend read it and told me I had a typo in the article.

So what’s hoisting in JavaScript? The short answer is that, conceptually, variable and function declarations are moved/hoisted to the top of their scope before code execution.

Variable declaration

When declaring a variable, only its name gets hoisted.

console.log(name);
var name = "whatever";
// The console logs undefined

Here’s how the above is interpreted, thus undefined.

var name;
console.log(name);
name = "whatever";

Function declaration

Unlike var, a function declaration hoists its actual definition too.

hello("world");
function hello(p) {
  return "Hello " + p;
}
// The console logs "Hello world"

Here’s how the above is interpreted, thus “Hello world”:

function hello(p) {
  return Hello  + p;
}
hello("world");

Function expressions

The variable name gets hoisted, but not the function assignment.

hello("world");
var hello = function(p) {
    return Hello  + p;
}
// TypeError: hello is not a function

Here’s how the above is interpreted, thus hello is not a function:

var hello;
hello("world");
hello = function(p) {
    return "Hello"  + p;
}

On ES6 and later

The hoisting behavior isn’t something that applies to latest ECMAScript gems:

  • let
  • const
  • class declaration
  • arrow function
// let
console.log(name);
let name = "whatever";
// Uncaught ReferenceError: name is not defined
// const
console.log(name);
const name = "whatever";
// Uncaught ReferenceError: name is not defined
// class declaration
const p = new Rectangle();
class Rectangle {...}
// Uncaught ReferenceError: Rectangle is not defined
// arrow function
console.log(doubled);
const doubled = [1, 2, 3].map(n => n * 2);
// Uncaught ReferenceError: doubled is not defined

Last but not least

Here’s what Brendan Eich said about JS hoisting:

var hoisting was an implementation artifact. source

function hoisting was better motivated: function declaration hoisting is for mutual recursion & generally to avoid painful bottom-up ML-like order source

The slides

Here are the slides, just in case.