Hoisting

Hoisting

In this blog, we’ll acquaint how the famed hoisting mechanism occurs in JavaScript. Before we dive in, let’s get to grips with what hoisting is.

In JavaScript, an important aspect of working with variables and functions is hoisting. If you’re looking for a detailed description of this aspect, then you’re in the right place. Let’s begin.

Hoisting

Hoisting is a JavaScript mechanism where variables and function declarations are moved to the top of their scope before code execution.

Inevitably, this means that no matter where functions and variables are declared, they are moved to the top of their scope regardless of whether their scope is global or local.

Note : JavaScript only hoists declarations, not the initializations.

Hoisting variables

ES5 - Var

In ES5, when you declare a variable using the var keyword, the scope of the variable is global if you declare it outside of a function or local in case you declare it inside a function.

The following is the JavaScript lifecycle and indicative of the sequence in which variable declaration and initialization occur.

Declaration –> Initialization –> Usage

var myName = 'Mahijendra'

console.log(myName)
// Mahijendra

Pretty straightforward, I declared a variable, initialized some value to it, and then logged it in the console.

Global Variables

According to the above statement, if I were to move my variable declaration to below the console.log() statement, it should work just fine. But that’s not the scenario. Let’s see why that is.

console.log(myName)
//undefined

var myName='Mahijendra'   // myName has been hoisted in this step.

We expected the result of the log to be: ReferenceError: myName is not defined, but instead, its output is undefined.

Why has this happened?

JavaScript has hoisted the variable declaration. This is what the code below looks like to the interpreter:

var myName;

console.log(myName); // Output: undefined
myName="Mahijendra" ;

However, in contrast, assigning a value to an undeclared variable implicitly creates it as a global variable when the assignment is executed. This means that all undeclared variables are global variables.

To demonstrate this behavior, have a look at the following:

function example() {
  a = 30;
  var b = 10;
}

example();

console.log(a);
/*
Accessible as a global variable outside example() function
Output: 30
*/

console.log(b);
/*
Since it was declared, it is confined to the example() function scope.
We can't `console.log() it out outside the example() function.
Output: ReferenceError: b is not defined
*/

The Window Object

The window object is supported by all browsers. It represents the browser's window.

All global JavaScript objects, functions, and variables automatically become members of the window object.

  • Global variables are properties of the window object.

  • Global functions are methods of the window object.

I'll show you guys an example later on in the below sections of how a window object is going to work.

Function scoped variables

As we’ve seen above, variables within a global scope are hoisted to the top of the scope. Next, let’s look at how function scoped variables are hoisted.

function john() {
  console.log(message);
  var message ='Hier to the iron throne!'
}

john();

Take a guess and see what your output might be?

If you guessed, undefined you’re right. If you didn’t, worry not, we’ll soon try to adapt knowledge in this topic below.

This is how the interpreter views the above code:

function john() {
  var message;
  console.log(message);
  message ='Hier to the iron throne!'
}

john();  // Ouput: undefined

So in the above code, the variable declaration var john whose scope is in the function john(), is hoisted to the top of the function.

So to dodge this downfall, we would make sure to declare and initialize the variable before we use it:

function john() {
  var John='Hier to the iron throne!';
  console.log(message);
}

john();  // Ouput: Hier to the iron throne!

ES6

Let

ES6 provides a new way of declaring a variable by using the let keyword. The let keyword is similar to the var keyword, except that the variables it declares are block-scoped:

In JavaScript, blocks are denoted by curly braces { } , for example the if-else, for, do-while, while,try-catch and so on:

if(condition) {
   // inside a block
}

See the following example:

let x = 1;
if (x == 1) {
    let x = 20;
    console.log(x); // 20:  reference x inside the block
}
console.log(x); // 1: reference at the begining of the script

Because the let keyword declares a block-scoped variable, the x variable inside the if block is a new variable and it shadows the x variable declared at the top of the script. Therefore, the value of x in the console is 20.

When the JavaScript engine completes executing the if block, the x variable inside the if block is out of scope, therefore, the value of the x variable that following the if block is 1.

Let’s start by looking at the let keywords behaviour.

console.log(example); // Output: ReferenceError: hoist is not defined ...
let example = 'champions leauge';  //the variable has been hoisted

Like before, for the var keyword, we expect the output of the log to be undefined. However, since the es6 let doesn’t take kindly on us using undeclared variables, the interpreter explicitly spits out a Reference error.

This ensures that we always declare our variables first.

However, we still have to be careful here. An implementation like the following will result in an ouput of undefined instead of a Reference error.

let example;

console.log(example); // Output: undefined
example = 'champions leauge';

Hence, err on the side of caution, we should declare then assign our variables to a value before using them.

JavaScript let and global object

When you declare a global variable using the var keyword, you are adding that variable to the property list of the global object. In the case of the web browser, the global object is the window.

See the following example:

var a = 10;
console.log(window.a); // 10

However, when you use the let keyword to declare a variable, that variable is not attached to the global object as a property. Here is an example:

let b = 20;
console.log(window.b); // undefined

Const

The const keyword was introduced in es6 to allow immutable variables. That is variables whose value cannot be modified once assigned.

With const, just as with let, the variable is hoisted to the top of the block.

Let’s see what happens if we try to reassign the value attached to a const variable.

const a = 3;

a = 2; // Let's reassign the value of a

console.log(a); // Output: TypeError: Assignment to constant variable.

How does const alter variable declaration? Let’s take a look.

console.log(football); // Output: ReferenceError: hoist is not defined
 const football = 'champions leauge';

Much like the let keyword, instead of throwing an undefined, the interpreter saves us by explicitly throwing a Reference error.

Hoisting Functions

JavaScript functions can be classified as the following:

  • Function declarations
  • Function expressions -Arrow functions

Function declarations

Like variables, the JavaScript engine also hoists the function declarations. It moves the function declarations to the top of the script. For example:

let x = 2,
    y = 1;

let result = multiply(x,y);
console.log(result);

function multiply(x, y) {
return x + y;
}

In this example, we called the multiply() function before defining it. The above code is equivalent to the following when it gets hoisted.

function multiply(a, b){
    return a + b;
}

let x = 2,
    y = 1;

let result = multiply(x,y);
console.log(result);

Function expressions

Function expressions, however, are not hoisted.

let x = 2,
    y = 3;

let result = multiply(x,y);
console.log(result);

var multiply = function(x, y) {
return x * y;
}

// output: TypeError: multiply is not a function

As we can see above, the variable declaration var multiply is hoisted but it’s an assignment to a function is not. Therefore, the interpreter throws a TypeError since it sees expression as a variable and not a function.

Arrow Functions

The following example changes the multiply function expression to the arrow function:

let x = 2,
    y = 1;

let result = add(x,y);
console.log(result);

var add = (x, y) => x + y;

// output : TypeError: add is not a function

The code also issues the same error as the function expression example because arrow functions are syntactic sugar for defining function expressions.

Similar to the functions expressions, the arrow functions aren’t hoisted.

Conclusion

Please feel free to leave any questions, comments, suggestions, or concerns below.

If this article was helpful,Tweet it

Thank you very much for reading!