DEV Community

Cover image for JavaScript : Execution Context and Hoisting
angDecoder
angDecoder

Posted on

JavaScript : Execution Context and Hoisting

Table Of Content

1. Intro
2. Execution Context

3. Hoisting in JavaScript
4. Scope, Scope chain and Lexical Environment



1. Intro

JavaScript --> JavaScript is a single-threaded, synchronous language.

  • Synchronous : In a specific order
  • Single-threaded : One command at a time

JavaScript is a high-level, interpreted programming language primarily used for creating dynamic and interactive content on websites. It requires a runtime environment to run in browsers. Most famous runtime environment is V8 engine(google).

JavaScript can also be used in server-side(Node.js), which also uses V8 engine and some additional features to make it run outside the browsers.


2. Execution Context

Execution Context --> It refers to the environment inside which the javascript code is executed.

Execution context is created in two phases :-

  • Variable Environment (Memory Component)
  • Thread of Execution (Code Componeent)

i. Variable Environment

This is the first phase of javascript code execution. In this phase memory allocation takes place for variables and functions.

It goes through the code line by line and allocates memory :-

  • For variables, it stores undefined as placeholder
  • For functions, it stores the entire code.

var a = 2;
var b = "hi";
function printHello(){
   console.log("hello world");
}

/*
  For first phase it stores ----

  **** GLOBAL EXECUTION CONTEXT ****
  a : undefined
  b : undefined
  printHello() : the whole code is saved

*/

Enter fullscreen mode Exit fullscreen mode

ii. Thread of Execution.

In this phase, code is executed line-by-line.

For variable, when initialisation is encountered it replaces the undefined with value.

When function call is encountered.

  • A new execution context is created inside the previous one.
  • When return is encountered then returns the control to the previous execution context.

var a = 2;
var b = "hi";
function printHello(){
   var c = 11;
   console.log("hello world");
}

// for below function call, a new exection context is created
// which also goes through the same two phases as we have discussed
printHello();

/*
For the second phase

*** GLOBAL EXECUTION CONTEXT ***
a : 2
b : "hi"
printHello() : code is executed

*/

Enter fullscreen mode Exit fullscreen mode

Now, that we have understood how execution context is created and how function can create it's own execution context, you might be wondering how javascript handles all these execution context. The answer is call stack.

Call stack --> It is a simple stack of execution context with the top element being the function currently being executed.

For each function call javascript creates a seperate execution context and pushes it inside the call stack. The very first execution context which is pushed inside the call stack is global execution context.

In the first phase of execution context only memory allocation for variables and functions. No code execution during this phase


3. Hoisting in JavaScript

Hoisting --> It is a concept which allows us to extract values of variables and call function even before they are initialised.

Let us understand this concept with the help of example :


console.log(a);   // undefined
hello();          // hello

var a = 10;
function hello(){
   console.log("hello");
}

Enter fullscreen mode Exit fullscreen mode

You might be wondering, how we can print variable a or how we could call function hello. We have already discussed the answer in the previous section.

During the first phase of memory allocation, the variable a was given value undefined and for function hello the whole code was saved. So, even before their declaration we can access them.

But there are some special case :-

  • For variables declared using let or const, we can't access the variables before their declaration. The time between their hoisting and declaration is called Temporal Dead Zone(TDZ). Accessing any variable in their TDZ, will give us Reference Error.

console.log(a);    // Reference Error
console.log(b);    // Reference Error

let a = 10;
const b = 20;

Enter fullscreen mode Exit fullscreen mode
  • For functions expression, they are treated as variables before their declaration and only as function after their declaration.


console.log(fun1);
// OUTPUT - undefined
// What would would happen if we print fun2 and fun3 ?
// ans -> they are in their TDZ, so it would give Reference Error

fun1();
fun2();
fun3();
// for the above function calls it would give *Type Error*.

var fun1 = function(){
   console.log('fun1');
}

let fun2 = function(){
   console.log('fun2');
}

const fun3 = ()=>{
   console.log('fun3');
}

Enter fullscreen mode Exit fullscreen mode

4. Scope, Scope chain and Lexical Environment

Block --> Multiple lines of code grouped together using curly braces {} is called a block.

Scope --> Region of code inside which the variables and function can be accessed.
Javascript mainly has types of scope : local scope and global scope.

Variables declared using var are function scoped and variables declared using let or const are block scoped. It means that var variables follow the scope of function only and not any other block. Whereas, let or const variables can't be accessed outside of block they are declared in( also includes function block ).

// the below block could be any block
// *for*, *while* etc.
{
   let a = 10;
   const b = 11;
   var c = 12;
}

console.log(a); // Reference Error
console.log(b); // Reference Error
console.log(c); // 12

// *c* can be accessed outside because it only follows
// function scope and here it is treated as if it was
// declared in globally 

Enter fullscreen mode Exit fullscreen mode

Lexical Environment --> The lexical environment is local memory along with lexical environment of it's parent. It consists of two components:

  • Environment Record: Stores the mapping of variable names to their corresponding values within the scope. For function scopes, it also includes a reference to the outer lexical environment.

  • Reference to the Outer Lexical Environment: A reference to the lexical environment in which the current lexical environment was created, forming a chain of lexical environments known as the scope chain.

Scope Chain --> The scope chain is a mechanism used to resolve references to variables within nested functions. When a variable is referenced within a function, JavaScript first looks for that variable within the local scope of the function. If it's not found there, it continues to look in the next outer scope, forming a chain until it reaches the global scope. This process is called scope chain traversal.


let a = "global a";

function outer(){
   let b = "outer b";
   let c = "outer c"

   function inner(){
      let b = "inner b"; 
      console.log(a);  // global a
      console.log(b);  // inner b
      console.log(c);  // outer c
  }
  inner();
}

outer();

// Lexical Chain
// NULL <- global <- outer <- inner 

Enter fullscreen mode Exit fullscreen mode

Top comments (0)