Variable and Function Hoisting

by kirupa   |   2 June 2017

One of the quirkiest things about JavaScript is this thing known as hoisting. We'll get to what it means in a bit, but let's set the stage for it by looking at some examples and figuring out what the right behavior should be. For our first example, take a look at the following code:

function foo() {
  return "Yay!";
}

console.log(foo());	

What do you think is going to be displayed in our console when this code runs? Here are three choices:

  1. undefined
  2. Error - foo isn't referenced
  3. Yay!

If you guessed Yay!, you would be right. There wasn't anything tricky there, so that was pretty straightforward. Let's kick things up a notch and take a look at a slightly modified version of our earlier code:

console.log(foo());

function foo() {
  return "Yay!";
} 

We are logging the value returned by our foo function before we actually even defined it. What do you think is going to be displayed in the console now? Will it be the same as before? Will it be something different?!! As it turns out, the answer is the same as before. Our console will print out Yay! as the output. Hmm...

It's time for our last example. Take a look at the following code:

console.log(bar);
var bar = 100; 

This seems similar to what we've seen so far, right? You would expect the value 100 to be printed to the console. The actual answer is a whopping undefined. What is going on here? By the end of this article, you will know all about what's happening and the role this hoisting thing plays.

Onwards!

JavaScript and Compiler Behavior

When you are running JavaScript, you have a JavaScript engine that takes the code you write and turns (aka compiles) it into the stuff that your computer understands and knows what to do with. As part of turning your code into something your computer understands, the compiler (aka the thing that does the turning) performs a variety of steps. One of these steps has to do with what happens when our compiler runs into any variable and function declarations. Depending on whether the declaration is for a variable or a function, the behavior is a little different. Let's look at each case separately.

Variable Declarations

Whenever our compiler encounters a block of JavaScript, the first thing it does is scan the entire block for any variable or function declarations. Take a look at the following example we saw earlier:

console.log(bar);
var bar = 100;

Our compiles looks at both of these lines, but before anything gets executed, it hones in on the variable declaration where we declare the bar variable:

console.log(bar);
var bar = 100;

At this point, what it does is promote the variable declaration to the beginning of the scope it is looking at. From the compiler's point of view, our code will look a bit like this:

var bar;
console.log(bar);
bar = 100;

This promotion of the declared variable is known as variable hoisting. The important thing to note is that only the declaration is hoisted. The initialization where we set the bar's value to 100 remains in the exact same spot. That explains what was going on with our example earlier and why we were printing an undefined. Because of hoisting, the bar variable exists when we try to log it. Because the variable isn't initialized at this point, what gets logged is a value of undefined.

Function Declarations

The behavior you saw with variable declarations is similar for functions as well. The major difference is that the entire function is hoisted - not an empty shell of it. Let's revisit our earlier example:

console.log(foo());

function foo() {
  return "Yay!";
} 

When our compiler scans this block of code, it hoists the foo function to the top. This is known as function hoisting. What you have is something that ends up looking as follows:

function foo() {
  return "Yay!";
}

console.log(foo());

That is why the output for this example is Yay! just as it would be if we wrote our code with the function definition specified before we try to call it. Pretty simple, right?

Some Hoisting Quirks

We are almost done here. Just because we understand how hoisting works doesn't mean there aren't some exceptions to what we've seen.

Function Expressions Need Not Apply

First, hoisting doesn't apply to function expressions. Look at the following example:

console.log(foo());
		
var foo = function() {
  return "Yay!";
}	

The output isn't going to be Yay! like we saw with plain old functions earlier. It is going to be a TypeError: foo is not a function.

The Temporal Dead Zone

There are cases where hoisting applies but the value is never initialized to something like undefined as we've seen with var. Those cases occur when you are working with let, const, and class. Take a look a the following block of code:

console.log(answer) // ReferenceError
let answer = "Correct";

console.log(ROLE) // ReferenceError
const ROLE = "user";

var foo = new AwesomeSauce(); // ReferenceError

class AwesomeSauce {
  constructor() {
    console.log("I exist!");
  }
}		

In all these cases, we would expect the variable or class to exist but simply not be initialized. That isn't the case. What you get with each example is a ReferenceError. It doesn't mean that using classes or variables defined using let or const don't get hoisted. They certainly do! The difference is that they remain uninitialized. This time between them getting declared and initialized has a pretty awesome name - the temporal dead zone.

Conclusion

For the longest time, we've always been told to declare our variables and functions first before initializing them. We've also been told to only use a variable or function after it has been initialized. None of that guidance changes. Just because JavaScript has a behavior around hoisting declarations to the top of the current scope doesn't mean we should make our code more difficult to read by relying on it. Think of hoisting as a warning for you to fix your code. It isn't intended to be something you look forward to using :P

The original version of this article forgot to mention the behavior with const and let. It also mentioned the hoisting behavior as an optimization as opposed to a behavior ingrained into how JavaScript works. Thanks to krilnon and senocular for pointing both of these issues out.

If you have a question about this or any other topic, the easiest thing is to drop by our forums where a bunch of the friendliest people you'll ever run into will be happy to help you out!

THE KIRUPA NEWSLETTER

Get cool tips, tricks, selfies, and more...personally hand-delivered to your inbox!

( View past issues for an idea of what you've been missing out on all this time! )

WHAT DO YOU THINK?

NEWSLETTER

No spam. No fluff. Just awesome content sent straight to your inbox!

Awesome and high-performance web hosting!
BACK TO TOP
new books - yay!!!