Core JavaScript: Hoisting

Core JavaScript: Hoisting

About the series

Core JavaScript is a series I'm writing that covers, as the name suggests, some of the core principles of JavaScript. It originally started as a study or reference material for myself, but I decided to share it with the community in the hopes that it will prove useful for others.

This series is intended for beginner JavaScript developers or more experienced developers in need of a refresher. I have a few topics planned out already, but I would love to hear from you if there are any topics, in particular, you would be interested to read about!


You might recall that in a previous article we came across the concept of hoisting in JavaScript. Essentially, hoisting is the process through which the declaration of a variable or function is moved to the top of its scope. It allows us to use variables and call functions before they appear in our code.

Declaration vs Assignment / Initialization

It is important to note that only declarations are hoisted, and initializations and assignments are not. Below is the difference, in case you need a refresher.

let number; // Declaration
number = 5; // Assignment
const name = 'John'; // Initialization

Consider the following examples:

var first = 1;
var second = 2;
​
console.log(first + " " + second); // Output: 1 2
var first = 1;
​
console.log(first + " " + second) // Output: 1 undefinedvar second = 2;

Because declarations are hoisted to the top of the scope but initializations are not, the second example essentially gets translated to the following:

var first = 1;
var second; // This is currently undefinedconsole.log(first + " " + second) // Output: 1 undefined
​
second = 2;

Var vs Let vs Const

If you've read my previous article about variable types, you know that var variables are hoisted to the top of their scope and initialized with the value of undefined. Some consider let and const to be non-hoisted, which is not entirely correct. In reality, let and const variables are indeed hoisted to the top of their scope, but they are not initialized.

This means that with var we can do something like this:

console.log(number); // undefined
var number = 5;

However, trying to do the same thing with let or const will throw an error:

console.log(number); // ReferenceError: number is not defined
let number = 5;

Functions

Hoisting works the same for functions as it does for variables - function declarations are hoisted, but function expressions are not.

hoistedFunction(); 

function hoistedFunction() { // Function declaration
  console.log('This will work!');
}
​
// Output: This will work!
nonHoistedFunction();

let nonHoistedFunction = () => { // Function expression
    console.log('This will not work...');
}
​
// Output: ReferenceError: nonHoistedFunction is not defined

If we use var instead of let, we still get an error, but of a different kind.

nonHoistedFunction();

var nonHoistedFunction = () => { // Function expression
    console.log('This will still not work...');
}
​
// Output: TypeError: nonHoistedFunction is not a function

Tip - Declare variables at the top

Hoisting is easy to forget about or overlook, which tends to lead to bugs and confusion. Declaring variables at the top of their scope helps you avoid errors and it keeps your code clean.


Practice

What are the outputs of these code snippets?

  1.  example();
     ​
     function example() {
       let name = 'Bon Jovi';
       console.log(name);
     }
    

    Answer:

     Bon Jovi
    

    Reason: example() is a function declaration, which is hoisted - therefore, it can be called before it appears in our code and everything will work as intended.

  2.  example();
    
     ​function example() {
       console.log(name);
       let name = 'Bon Jovi';
     }
    

    Answer:

     ReferenceError: Cannot access 'name' before initialization
    

    Reason: let variables, despite being hoisted to the top of their scope, are not initialized. Therefore, we are warned that we cannot use name before it is initialized.

  3.  example();
     ​
     let example = () => {
       let name;
       console.log(name);
       name = 'Bon Jovi';
     }
    

    Answer:

     ReferenceError: example is not defined
    

    Reason: Function expressions are not hoisted, therefore we cannot use them before they appear in the code. Because we are using let here, we get a ReferenceError.


Thank you for reading the third article in my Core JavaScript series! If you found this useful, if you have anything to add, or if I made any mistakes - I would love to hear from you! I'm also very open to suggestions for future topics for the series. 😊

Expect to see the next part soon. Thanks and see you there!