Hoisting in JS: Explanation + Examples

Table of contents

No heading

No headings in the article.

Hoisting is why I hate yet love JavaScript at the same time. It's why I want to quit programming and continue building projects together. But what is hoisting? Hoisting basically allows you to use functions and variables before they're declared in the code. Let's learn more about it and its advantages and disadvantages in this article.

So, we know that this is typically how we would implement functions and variables.

var foo = 'foo'

function bar(str) {
    console.log(str)
}

bar(foo) // foo

Beginner-level javascript, I know. But what if we wrote the code like this?

foo = 'foo'

bar(foo)

function bar(str) {
    console.log(str)
}

var foo

What will the output be in this case? This'll surprise you, but we'll have the same log as the previous code! Why does this happen though?

JavaScript hoists declarations to the top. Any declaration, variable or function, is hoisted to the top of the code. So in the above code, var foo gets hoisted to the top of the code. Also, function bar(str) {} gets hoisted to the top of the code. If you think about it now, the code does indeed work.

What will happen in each of the following examples?

// Example 1

console.log(foo)

var foo = 'foo'

// Example 2

console.log(foo)

Obviously, example 2 will throw a ReferenceError. Example 1 will log undefined instead. This is because example 1 is interpreted by the javascript interpreter as follows:

var foo

console.log(foo)

foo = 'foo'

As you can see, only the declarations are hoisted to the top, not the assignments.

However, we've used var so far. let and const have different behaviours. With var, variables are initialized to undefined in their declarations that are hoisted. Also, you need to always declare a variable for it to be hoisted. For example, the following code outputs a ReferenceError:

console.log(bar) // ReferenceError
bar = 'bar'

It's quite weird that JavaScript lets us use a variable before we even declare it! For this purpose, ES2015's let and const have different behaviours. What do you think the following code will output?

console.log(foo)

let foo = 'foo'

If let were replaced by var, we'll get undefined as expected. However, this code will actually output ReferenceError: Cannot access 'foo' before initialization. So, the variable might have been hoisted, but... not initialized? The reason for this is the Temporal Dead Zone (TDZ). The TDZ starts at the variable's enclosing scope and ends wherever the variable is declared.

Here's what the TDZ looks like for our above example:

// Start of TDZ

console.log(foo)

let foo = 'foo'

// End of TDZ

Since we're accessing our variable inside the TDZ, we get a ReferenceError. I recommend you read this article about the TDZ I found online.

Functions are also hoisted in JS. But only function declarations, not function expressions. This is because function expressions are stored in a variable, and of course, variable declarations are not hoisted.

foo() // Uncaught TypeError: foo is not a function
var foo = function () { }

bar() // Uncaught ReferenceError: Cannot access 'bar' before initialization
const bar = function () { }

foobar() // Uncaught ReferenceError: foobar is not defined

According to me, variable hoisting creates confusion, but function hoisting is pretty useful. I've opened a variety of repos on GitHub, and many times have I been confused about the absence of a function declaration, only to scroll down to the bottom of the file and find it!


Thanks for reading! I really hope you enjoyed this article, and if you did, click the favourite button and write a helpful comment for me, I'd love some feedback!