JavaScript 101: Hoisting

Earlier this week we went through the specifics and quirks of JavaScript’s notions of scope, and touched particularly on function scope and block scope.

The focus of today’s post is closely related to all that, and introduces a new concept, that of hoisting. Not only is hoisting an important aspect of the language, the implications of which one comes across pretty much on a daily basis, but it is also a constant presence in JavaScript interview questionnaires. Even more reason for its specifics and implications to be fairly clear to one and all.

Why so confusing?

Let’s begin by looking at some code.

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

The assumption here might be that var x; would redefine x and that the console.log(..) statement would log undefined. But in fact, 10 will be logged.

Now, it gets even trickier. Have a look at the following:

console.log(x); //undefined
var x = 10;

One might expect that 10 would be logged. Or, that since x is used before it’s declared, we’ll get a RefferenceError. Instead, undefined is logged.

What is happening here?

Hoisting

Well, it’s all down to the manner in which JavaScript interprets code.

The JavaScript engine compiles the code prior to interpreting it, and in that process it matches all declarations (variable and functions) with their corresponding scopes. This happens for declarations only, hence the apparently inconsistent ways in which the snippets above seem to work. Declarations are processed at compilation time, while assignments and all other logic are left as they are and interpreted at execution time.

In essence, declarations are moved from where they appear in the code to the top of their containing scope. Hence, the term of hoisting.

We should stress on the fact that hoisting works on a per scope basis, and will not hoist declarations to the top of the codebase, but to that of their enclosing scope (function).

Also an important aspect of hoisting relates to function hoisting. The key thing to remember here is that function declarations are hoisted, but function expressions are not. Consider the below:

foo(); //works, as function declarations are hoisted

function foo() { //function declaration
...
}

bar(); //TypeError! variable bar is hoisted, but has no value as the below is a function expression

var bar = function baz() {
...
}

Also worth mentioning is the fact that function declarations are hoisted before variable declarations. This is particularly relevant when you come across duplicate definitions in the same scope, which can lead to unexpected results and should ideally be avoided.

foo(); //hi

var foo;

function foo() {
  console.log("hi");
}

foo = function() {
  console.log("bye");
}

The above is interpreted as:

function foo() {
  console.log("hi");
}

foo(); //hi

foo = function() {
  console.log("bye");
}

That’s it!

While it’s tempting to think of hoisting as just another one of those JavaScript quirks one doesn’t entirely understand, being aware of its mechanisms, combined with a deep knowledge of of JavaScript scope, opens the door towards cleaner, clearer and bug-free code.