Hoisting is a behavior in JavaScript where variable and function declarations are “hoisted” to the top of their scope before code execution. This can result in confusing behavior, such as the ability to call variables and functions before you wrote them. In this article, we will review both how hoisting works and why by understanding the JavaScript interpreter’s two pass process.

Note that code samples are intended to be run as complete files, not in the JavaScript console of your web browser. The simplest way to run JavaScript code is to install Node and execute file updates from the command line. So if your file is called ‘ex.js’ you can run it by typing node ex.js.

Variable Declarations

The traditional way to declare a variable in JavaScript is with the var keyword. We need to declare variables before we use them, otherwise we receive an error:

myVariable; // ReferenceError: myVariable is not defined

Once a variable has been declared it exists in memory but it is has no value. In JavaScript this is represented as undefined.

var myVariable;
console.log(myVariable); // undefined

We can declare a variable and assign it a value in one concise line as follows:

var myVariable = 10;
console.log(myVariable); // 10

Variable Hoisting

Given what we know about variable declarations, what do you expect the following code will do?

console.log(myVar);
var myVar = "A hoisted variable";

The logical expectation is that calling myVar before it has been formally declared or assigned will result in a ReferenceError. But that’s not what happens. The result of calling console.log(hoist) is in fact undefined!

In order to run our code, the JavaScript interpreter takes two passes through the code. On the first pass it looks for variable and function declarations which it then “hoists” to the top of the scope; only then, on the second pass, does it make assignments and execute the code. This means that in the eyes of the JavaScript interpreter, the code above really looks like this:

var myVar;
console.log(myVar);
myVar = "A hoisted variable";

First myVar is declared but not assigned and therefore has the value undefined. Next we call myVar and receive its value, which is undefined. Finally we assign the variable the value of “A hoisted variable”.

Let’s add a second console.log to the code after the assignment so we can understand things more clearly:

console.log(myVar);
var myVar = "A hosted variable";
console.log(myVar);

In the eyes of the JavaScript interpreter, this is equivalent to:

var myVar;
console.log(myVar); // undefined
myVar = "A hoisted variable";
console.log(myVar); // "A hoisted variable"

Function Hoisting

Functions declarations are also “hoisted” to the top of the scope, which means you can call functions before they are declared in your code.

Consider the following example:

hoistMe();

function hoistMe() {
  console.log("I'm a hoisted function.");
}

The output is “I’m a hoisted function.” even though the function itself comes after its invocation on the first line.

This occurs because the JavaScript interpreter on its first pass notices all function/variable declarations and hoists them to the top of the scope. On the second pass it executes the code, which involves making assignments, invoking functions, and so on. For the JavaScript interpreter, the above code really looks as follows:

function hoistMe() {
  console.log("I'm a hoisted function.");
}

hoistMe();

Note that just as variable assignments are not hoisted, so too function assignments are not hoisted either. A good example of a function assignment is a function expression, which takes the form var myFunc = function () {}.

Let’s rewrite the code above to make it a function expression rather than a function declaration.

hoistMe();

var hoistMe = function () {
  console.log("I'm a hoisted function.");
}

The result is TypeError: hoistMe is not a function. The JavaScript interpreter knows that the function hoistMe has been declared (otherwise it would have thrown a ReferenceError), but the variable has not been assigned a value. Therefore when we try to invoke it like a function, the JavaScript interpreter complains and says, “Hey, this isn’t a function!” In fact it’s not defined as anything yet.

Let’s confirm this with the code below, which confirms we receive a ReferenceError for hoistMe:

typeOf(hoistMe); // ReferenceError: typeOf is not defined

var hoistMe = function () {
  console.log("I'm a hoisted function.");
}

Rewriting the code as the JavaScript interpreter sees it looks as follows:

var hoistMe;
hoistMe();
hoistMe = function () {
  console.log("I'm a hoisted function.");
}

Order of Precedence

Variable assignments take precedence over function declarations which take precedence over variable declarations. That’s a mouthful, huh?

Another way to think of it as follows:

variable assignment > function declaration > variable declaration

Consider the code below where we try to declare a variable and a function to myVar:

function myVar(){
  console.log('I am a function.')
}
var myVar = 'I am a variable';
console.log(myVar); // "I am a variable"

It makes no difference what order we put our variable/function declarations in.

var myVar = 'I am a variable';
function myVar(){
  console.log('I am a function.')
}
console.log(myVar); // "I am a variable"

The internal JavaScript interpreter will always give precedence to a function declaration.

But a variable assignment will always take precedence over a function declaration:

var myName = 'William';
function myName(){
  console.log('Will');
}
console.log(myName); // "William"

Conclusion

Remember that the JavaScript interpreter makes two passes over the code. First it finds all variable and function declarations, which are “hoisted” to the top of the scope. Second it goes top-to-bottom executing the code: making assignments, invoking functions, etc. With this mental model, the behavior of hoisting makes much more sense.

And when it comes to order or precedence, you just need to remember: var assignment > func declaration > var declaration.