Hoisting in JavaScript.

Hoisting in JavaScript.

ยท

8 min read

Hoisting in Javascript ๐Ÿค”,what is hoisting , why I should learn this concept, what its use?. to understand hoisting we need to understand how the javascript code is executed.

understanding JS code execution

considering Browsers , Every Browser that we open today will be having javascript run time environment, JS runtime environment will be having web API's ,callback Queue, JS Engine...,JS Engine is in which our code will be given a memory allocation and then executed. lets talk more about how code is executed in JS Engine.

execution context :

when browser hits the script file and tries to run it, a Global Execution Context (GEC) is created, It is the environment created by JS engine for doing memory allocation (creation Phase)and code execution(execution phase).only one GEC will be created for entire script which is not inside functions, for every functions a separate execution context is created that is called as function execution context (FEC).these execution context will be maintained by the call stack,FEC is created whenever the function is called.

creation phase :

image.png Fig:1 GEC creation phase example.

Above image showing the developer tools sources tab with the below code and you can see that even before any line of code is executed a global scope is created mean a memory allocation happened.

// values initialized by JS engine in creation phase
var agevariable = 6; // undefined
function agefuntion(){ // function agefuntion(){ // console.log(agevariable); }
    console.log(agevariable);
}
agefuntion();

considering the above code and by observing the fig-1 we can see that in global scope the variable agevariable is allocated with undefined and function agefunction is allocated with entire function itself.so this is done by JS Engine,JS engine will go through entire script once and it will allocate a memory for every variable in script file and assigns/initialize its value with undefined for time being (in the sense before execution),as well as for functions entire function is stored before defined.

okay fine, until now we have seen only for the function declarations ,will that be same for function expressions ?, now look at the below code example and fig 2. image.png Fig:2 memory allocation for function expressions.

// values initialized by JS engine in creation phase                                                  
var agevariable = 6; //undefined
var agefuntion=function(){ // undefined
    console.log(agevariable);          
}                                                       
agefuntion();

From the above fig2 we can see that value of agefunction is given as undefined, it is because when JS engine skims the enitre code in creation phase it just looks up to before equal operator and considers it as variable and we know that for variables value is assigned as "undefined".

code execution

Now lets go to the second phase that is code execution(also called thread of execution). javascript is a single threaded(means executes one line at a time) language. so let us try to mock this behavior with below snippet.

let us consider the below example and do the line by line execution.

// values initialized by JS engine in creation phase
var agevariable = 6; 
function agefuntion(){ 
    var agefunctionVariable=2;
    console.log(agevariable);
}
agefuntion();

1) image.png fig - 3 Before code executed.

before even single line execution (creation phase) look at the variable and function initialized with default values and attached global object.

2)image.png fig - 4 After executing the line 2.

press F9 in developers tools ,now line 2 is executed the variable agevariable is initialized and stored its value as same in scope.

3)image.png fig - 5 FEC creation phase.

then again pressing F9 , u can see that code execution goes inside the function and other execution context is created, now no code is executed inside function and we can memory allocation happened to variable agefunctionVariable,after doing F9 again line no:4 will be executed.

4)image.png fig - 6 Code execution inside function.

now look at the variable value, initialized with value 2. now do one more step (F9) line 5 is executed it consoles the value as 6 because of lexical scope (functions having access to parent scope variables(global scope in our case) but reverse is not true).then do F9.

5)image.png fig - 7 Code execution moved to main execution context.

here if we observe ,after function execution is completed, code execution is now shifted to the main thread and FEC is deleted ,now we can see only GEC. lets get small introduction of callstack to get idea behind how code execution takes from GEC to FEC and FEC to FEC..

Small intro about call stack:

All these FEC and GEC is tracked by call stack as we discussed, call stack works in an LIFO (last in first out ) manner. we know GEC is created when script given to run, this GEC is considered as main thread/main function. this main function is pushed into call stack for execution, if any function is called from main thread a FEC is created and FEC is pushed to top of call stack and it is given priority of execution according to call stack(as it follows lifo).

similarly if any function is called form function or from FEC other FEC is created and it is pushed to top for execution. FEC which is at the top of callstack executed then its execution context is popped out from call stack and control moves to one from which its called. similarly if the entire script execution is done GEC is removed from callstack.

Hoisting

After all the discussion ,lets understand hoisting concept. because of the above mentioned memory allocation /creation phase, hoisting concept is available.

we can define hoisting in JS is a concept in which we will be able to use the variables /function before variable initialization and function is defined. as we discussed, in creation phase these variables /functions/class declarations attached to the top of their scope making it available to use even before initialization this is called hoisting.

Hoisting in Variables

  1. hoisting in var : Analyse the outputs of below code example
    console.log(year); //undefined
    var age = 5;
    console.log(year) //undefined
    var year = 2000;
    console.log(year) // 2000
    

    Example - 1: hoisting in var

    Here first and second console.log logs undefined, the var variable year is stored in memory and assigned with undefined value, so therefore even before executing the line var year = 2000 gives the default value stored by JS, after that line when we try to console year it outputs 2000 as its default value undefined is replaced with value 2000.

    calcAge();
    console.log(age) //undefined
    var age = 5 ;
    function calcAge(){
      console.log(age); //undefined
    }
    

    Example - 2: hoisting in var

    above eg is slight tricky, if you understand the concept it is easy, lets dig in . when ever calcAge() function is called ,at that instant code execution is jumped inside the function calcAge and the lines after function call is paused mean not yet executed as the control of execution moved inside function. now function looks up its own FEC / scope whether any variable are declared or not , if not then it looks up in its parent scope , as we know that this function as parent scope which is global then it finds the age variable stored with undefined value (since var age = 5 is not executed)and outputs value as undefined.

  2. hoisting in let/const :

    let and const variables are hoisted ,and separate scope is also created for variables, but not accessible until the let variable execution reaches until the declaration and const variable until the declared and initialized.it is due to variables are in Temporal dead zone(TDZ). the time between the variables entering scope and until being declared/initialized is called Temporal dead Zone.

    when we try to access these variables we will get error like as shown in example

    console.log(x); // ReferenceError: Cannot access 'x' before initialization
    let x ;
    

    error message its self given us clue that variable are stored but your accessing before initializing. if we compare the error message with "y" used in console.log which is not declared any where in code. gives us below message.

    console.log(y); // ReferenceError : y is not defined
    

    from below code snippet we can see that after reaching line let x;

    //temporal dead zone  for x starts here ,temporal dead zone  for z starts here 
    console.log(" do log"):
    //temporal dead zone  for z ends here 
    let x ;
    console.log(x); //undefined
    x=10;
    console.log(x); // 10
    
    // const z; //gives us type Error 
    // console.log(z) // before executing below line gives  ReferenceError: Cannot access 'z' before initialization
    
    //temporal dead zone  for z ends here 
    const z= 12; 
    console.log(z) //12
    

    Example :hoisting in let/const

Hoisting in Functions

  1. function declarations:

    function declaration are called as fully hoisted ,functions declarations are stored as it is, in memory .

    calcAge(5); 
    function calcAge(age){
    console.log(age)  // 5
    }
    

    Example-1 function declaration in hoisting

  2. function expressions

    function expression are not hoisted.

    calcAge(5);  // type Error
    var calcAge = function(age){   
    console.log(age)  
    }
    

    Example-2 function expression

    From the above code snippet , if we dig in, In memory allocation phase var calcAge value assigned with undefined value, because memory allocation done only for declarations, it just see before equalto operator and understands as variable only ,var variable is allocated with undefined as we know. so when we try to access the variable, we will get Type Error.

Hoisting in classes

  1. class declarations:

    similarly to let and const variable ,classes are only allocated with memory but not accessible until class is defined. when we try to access the class variables we will get reference error as "cannot access before initialization".

    var student = new Student('sangy',2);
    console.log(student);  // ReferenceError: Cannot access 'Student' before initialization
    
    class Student {
      constructor(name,rollNo){
           this.name=name;
           this.rollNo= rollNo;
       }
    }
    

    Example-1 class declaration in hoisting

  2. class expressions:

    class expressions are not hoisted since it does same as function expression , and value of class assigned variable will be stored as undefined ,when we try to access the class expression before defining, it will throw a type error.

    var student = new Student('sangy',2);
    console.log(student);  // Type Error
    
    var Student = class {   //undefined
      constructor(name,rollNo){
           this.name=name;
           this.rollNo= rollNo;
       }
    }
    

    Example-2 class expression .

why need to learn hoisting

we as a developers it is important to know this concept to avoid /resolve any bugs, we can also use the situation of hoisting's like var variables and functions declarations which are accessible before initializations. but it is always recommended that we need to declare variables before accessing/using them in our code base.

Hope you understand the concept .Thanks.

ย