RemixNode's Blog

TwitterGitHub
JavaScript Hoisting Internals

JavaScript Hoisting Internals

Introduction

Welcome to the third post of the series, JavaScript: Cracking the Nuts. In this post, we will learn about another fundamental JavaScript concept called Hoisting.

As the series's focus is to get to the basics and internals of every concept, we will try exploring the internals of Hoisting here than just scratching the surface.

If you haven't got a chance to read through the earlier posts of the series yet, you can find them here:

What do we know so far

Here is some recap from the last few posts of the series:

  • In JavaScript, the source code typically goes through several phases before it is finally executed. The phases are, Tokenizing, Parsing, and Code Generation.
  • Whenever a JavaScript code runs, it creates something called Execution Context. It helps in determining what code is currently running and helps in allocating memory for every function and variable declarations it finds on the way.

The mechanism of allocating memory for the functions and variable declarations within an Execution Context is called Hoisting.

Demystifying some Myths

The word Hoisting is confusing and misleading in the context of what is actually happening here. That is one reason why many of the explanations of Hoisting are focused on the fact that variables and functions are hoisted.

In plain English, Hoisting meaning, raise (something) using ropes and pulleys. At some point, one may start believing that things(variables and functions) actually being hoisted by moving their position up by the JavaScript engine.

shock.gif

Hey, relax, nothing like that ever happens! Honestly, no code physically gets hoisted. It is all about how memory is allocated for functions, variable declarations in an Execution Context's Creation Phase.

We will see that with examples soon.

Variable Hoisting

Consider this simple code:

console.log('name is ', name);
var name;
name = 'tom';
console.log('name is ', name);

What's the expected output of the above code? Well, it is easy:

name is  undefined
name is  tom

The question is, why? We have accessed the variable name well before it has even been declared. Like many other programming languages, we should have got an error. But instead, we got undefined.

in JavaScript, the code execution context is divided into two phases:

  • Creation Phase
  • Execution Phase

In the creation phase, the memory gets allocated for the variables and initialized with a special value called undefined.

The meaning of undefined is, a variable has been declared, but it has no value assigned.

In the code example above, the creation phase declared the variable name by allocating memory for it and marked it as undefined. This phenomenon is called Variable Hoisting in JavaScript.

Later in the execution phase, the value tom gets assigned to the variable name, and the console log statements get executed. As the creation phase happens before the execution phase, we find the variables are already declared, i.e., created in memory(as in Hoisted).

Function Hoisting

Function hoisting follows a similar path to variable hoisting. In function hoisting, JavaScript Execution Context's creation phase put the function declaration into memory. Let us understand it with this example:

// Invoke a function, chase()
chase();

// Declare a function, chase()
function chase() {
  console.log('Tom chases Jerry!');
  // Invoke a function, caught();
  caught();
}

// Declare a function, caught()
function caught() {
  console.log('Tom caught Jerry :(')
}

The creation phase of the Execution Context creates a memory for the function chase(), and the entire function declaration has been put into the memory. In the execution phase, the entire function of the memory can be executed.

As we know, the function creates its own execution context(Function Execution Context), the mechanism remains the same in the function execution context. First, create a memory for caught() and put the declaration into it. Later execute it.

function_hoisting.gif Created using https://tylermcginnis.com/javascript-visualizer/

Hoisting Rules

A general rule of thumb is to always define functions, variables, etc., before using them in code. This can help in avoiding surprises and debugging nightmares.

There are a few guidelines and checks already put into JavaScript language to safe-guard from the pitfalls of using Hoisting without realizing it.

  • JavaScript only hoists declarations, not initialization. With this, the following code is going to break:

    test();
    
    var test = function() {
      console.log('I am being tested');
    }
    

    It will throw the following error because the test declaration is hoisted and initialized with undefined as a value. It was never assumed to be a function. It was, in fact, hoisted as variable hoisting, not function hoisting.

    hoisting_error_1.png

  • let and const declarations are hoisted, too, but they are not initialized to undefined like var. See the example here:

    console.log(foo);
    let foo;
    

    This will throw the following error but, it will just run fine with var:

    hoisting_error_2.png

Conclusion

As mentioned above, always define functions, variables, etc., before using them in code. Do not rely on Hoisting much. At the same time, it is important to understand the underlying concept of why certain things are behaving in specific ways.


I hope you find the article useful. Please Like/Share so that it reaches others as well. If you enjoyed this article or found it helpful, let's connect. You can find me on Twitter(@tapasadhikary) sharing thoughts, tips, and code practices.

To get e-mail notifications on my latest posts, please subscribe to my blog by hitting the Subscribe button at the top of the page.

In the next post of the series, I'll be explaining another fundamental concept called Scope. Stay Tuned.