Site icon Primathon

Hoisting In JavaScript

A detailed explanation to what is hoisting in JavaScript with simple examples.

Introduction

Hoisting in JavaScript is a behaviour because of which variables or functions can be used before declaration. That is, all the declarations are moved to the top of the scope(global or local scope) before code execution.

In reality, JavaScript do not physically moves the declaration to the top. Instead, all the declarations stay exactly where they were typed in the code. So, when one executes the JavaScript code, the JavaScript engine first creates the execution context. This execution context is created into two phases:

During Phase 1 that is Creation Phase, memory is allocated to all the variables or functions and make them available before the code is executed. It is to be noted that initially variables are assigned with the value of undefined, whereas functions contain their own definition.

Most importantly do remember that during Phase 1 only declaration of variables or functions take place and not initialisation. Initialisation only takes place in Phase 2 of execution context.

Hoisting Variables

Variable Hoisting is that variable declarations are made even before the code is executed. As variables in JavaScript can be declared by var, const and let so, lets understand how they are hoisted one by one.

var

Lets understand how hoisting of a variable declared with var takes place with an example:

console.log(name);   // undefined
var name = "Ram";

While executing above code snippet the output will be undefined which was not expected, should have got ReferenceError. This output resulted because during creation phase of the execution context, variable name is allocated a memory and initialised with a value of undefined. So during execution phase the code was viewed as follows:

var name;
console.log(name);  // undefined
name = "Ram";

That is why even before declaring the variable in the code one gets to see its initial value(undefined).

In hoisting, variable declarations are accessible only in the immediate scope. So when the variable is used inside the function, it is hoisted only to the top of the function. Lets see an example:

function yourName() {
  name = "Ram";
  console.log(name); // Ram
  var name;
}
yourName();
console.log(name); // ReferenceError: name is not defined

From the above code, it can be clearly understood that variable name is hoisted only in its scope that is the function it is declared in. So name become local variable for function yourName and is available there but not outside the function. That is why when name is accessed outside yourName function an error is thrown.

let

Unlike variables declared with keyword var, variables declared with keyword let are hoisted but not initialised to undefined or any other value. It is also important to keep in mind that variables declared with keyword let are block scoped and not function scoped. Now, to understand the behaviour of let towards hoisting, consider the following example:

console.log(name) // ReferenceError: Cannot access 'name' before initialization
let name = "Ram";

The error that comes after executing the above code means that variable name is assigned a memory but not initialised and until and unless a value is assigned to a variable it can not be accessed.

const

Same as variables declared with keyword let, variables declared with keyword const are hoisted but throw an error when accessed before evaluation. Consider the following example:

console.log(value) // ReferenceError: Cannot access 'value' before initialization 
const value = 300;

Above code snippet gives an Reference error because variable const is declared in the memory but not initialised with any value.

NOTE: Undeclared variables do not exist in the memory until code initialising it is executed. When the code initialising the undeclared variable is executed , this variable is created as a global variable. Understand this with an example:

function personalDetails() {
  name = "Ram";
  var age = 20;
}
personalDetails();
console.log(name);  // Ram
console.log(age);  // ReferenceError: age is not defined

From the above code snippet it could be understood that all undeclared variables behave as global variables after the code initialising the variable is executed.

Hoisting Functions

If ever wondered why you were able to call functions before writing them, then keep reading…

Hoisting of functions mean that functions can be called even before declaring them. As discussed earlier during creation phase of execution context, functions are allocated memory and contain their definition as value. That is why whenever a function call is made before it is written no ReferenceError is thrown. Lets understand this with an example:

let num1 = 50, num2 = 10;
console.log(multiply(num1, num2)); // 500
function multiply(x, y) {
  return (x * y);
}

In the example above, multiply() function is called before defining it and still we get the result without any ReferenceError. So what happens is during creation phase, memory is allocated to multiply() function with its definition as value. Now when the code execution phase starts and reaches the multiply() function call, the JavaScript engine already knows that this function exists in the memory. That is why no error is thrown.

Hoisting of Function Expressions

Unlike function declarations, function expressions are not hoisted. Lets understand this with an example:

let num1 = 50, num2 = 10;
console.log(multiply(num1, num2)); // TypeError: multiply is not a function
var multiply = function(x, y) {
  return (x * y);
}

The above code gives an error saying TypeError: multiply is not a function. This error occurs because during creation phase variable multiply is allocated memory with a value undefined. When code execution phase starts and execution reaches to function call JavaScript still has multiple as variable with value undefined and not function definition. That is it gives error that multiply is not a function because technically it is still a variable.

Hoisting of Arrow Function

Same as function expressions, arrow functions are also not hoisted. To understand this consider the following example:

let num1 = 50, num2 = 10;
console.log(multiply(num1, num2)); // TypeError: multiply is not a function
var multiply = (x, y) => x * y;

The above code snippet gives a TypeError on execution. This is because during creation phase of execution context, multiply is treated as a variable and assigned with the value of undefined. When code execution phase starts, multiply is still a variable with undefined as value that is why could not be called as a function.

Hoisting Classes

In JavaScript, classes can be declared by two ways – Class Declaration and Class Expression. Lets see the effect of hoisting on each way one by one.

Class Declaration

Class Declarations are hoisted. However, they are not initialised. Let understand hoisting of class declaration with an example:

var person = new Person();
person.name = "Ram";
console.log(person); // ReferenceError: Cannot access 'Person' before initialization
class Person {
    constructor(name) {
      this.name = name;
    }
}

The example above gives a ReferenceError because the class Person is declared in the creation phase of execution context but not initialised. To avoid this error, always declare classes before accessing them.

Class Expression

Similar to function expressions, class expressions are also not hoisted. Lets take an example to understand it:

var person = new Person();
person.name = "Ram";
console.log(person); // TypeError: Person is not a constructor
var Person = class {
    constructor(name) {
      this.name = name;
    }
}

The above code snippet gives a TypeError on execution because of the same reason as that of function error. During creation phase of execution context, Person is considered as a variable with undefined. Since undefined value can not be used as a class, an error is thrown.

Summary

I hope this article provided a good source of introduction and understanding to the concept of Hoisting in JavaScript.

Exit mobile version