How JS actually works (Part-1)

shouvik sarkarshouvik sarkar
10 min read

Hey there JS champions, I know you have written hundreds or may be thousands of lines of codes. But have you ever thought that how these lines of codes are actually being executed?

When you write:

var a = 20;
console.log(a); // output: 20

or

console.log(a); 
var a = 20;

// output: undefined

How you actually get these results?

Or,

console.log(a); 
let a = 20;

// output: ReferenceError: Cannot access 'a' before initialization

Why you actually got this error?

If you don’t know or never thought about this, I’m sure you are really curious now. Let’s dive into this and find out how this actually works.

Global Execution Context:

Before jumping into how each of those codes are executed, we’ll have to understand what Global Execution Context is and how it works.

Global Execution Context is the base execution context for all JavaScript code. It is a base that is not inside any function. This is the default context created when a JavaScript engine starts running a script. GEC also sets the environment for the JavaScript code.

Image: Pictorial representation of Global Execution Context.

NOTE: (For curious people), in this GEC the global variable (Global object in Node and window in browser) is set also the value of this is also set here.

Phases of GEC:

In GEC there are 2 main phases:

  1. Memory allocation phase or Creation phase

  2. Execution phase

Memory allocation phase or Creation phase:

This is the first step or phase of JavaScript’s execution context. In this phase variables and functions are assigned in it’s current scope. Although the value is not assigned in this phase. The variables are initialized with undefined and the functions are stored in the memory with the entire function definition.

Execution phase:

This is the second phase of the of two phases of GEC. In this phase JS engine executes the code line by line. In this phase the values are assigned to there respective variable. Also o execution of the function calls happen in this phase.

Hoisting:

Hoisting is a default JavaScript behavior of conceptually moving declarations of variables and functions (not initialized) at the top of the scope (global or local scope) during “Memory Creation Phase” before execution.

Practical example

Example-1:

console.log("The value of a is", a);
var a = 12;
// output: The value of a is undefined.

Notice how a is printed before the declaration still we got a value undefined. This is because of hoisting. The variable was put at the top of the scope.

Yeah I know the value printed is not the value allocated and reason will be revealed later.

Example-2:

func();

function func(){
    console.log("Hello")
}

// output: Hello

As you can notice we called the function before it was even declared still got the output right. How? Well because of hoisting. It doesn’t matter where the function is declared and where called, for the execution context the function body is at the top of the scope. So we got the result out perfectly. Follow the next examples and you’ll understand hoisting even better.

NOTE: You might be wondering why function gave proper output instead of undefined. Well we will talk about that dedicatedly in the next article.

How actually code lines are executed?

Now that we understand the basics of GEC and phases, we are ready to understand how the code lines are executed

Example-1:

var a = 12;
console.log("Value of a is", a); // output: Value of a is 20

Step-1:

In the first step a GEC(Global Execution Scope) is created, with two phases in it. (Memory Allocation Phase and Execution Phase)

Step-2:

In this step JS engine traverses through the code and allocates memory for all variables and functions. The variables declared with var are hoisted and initialized with value undefined .

Since there’s only one value in our example, only one variable is hoisted here.

Step-3:

In this step the JS engine executes the code in Execution phase, line by line.

Executing the code:

Value updating in memory: The first line var a = 12; updates the value of a in memory from undefined to 12.

Now the second line is executed. console.log("Value of a is", a);. Since the value is already updated with 12 The value of a while executing will be 12.

As you can see the code is executed and this is how you get the result after code execution.

Once the task is completed and the code is executed. GEC gets deleted.

Example-2:

console.log("The value of a is", a); 
var a = 20;

// output: The value of a is undefined

Ok so how in this one we’re getting an undefined , when we are printing the variable before declaring it. with var?

The “memory creation phase” for this piece of code is also exactly same as the previous one. “But why undefined is the output then?” Well because how “Execution Phase” is being executed.

Memory phase:

In “Memory Phase” as usual the variables are allocated into the memory with undefined as the value.

Execution Phase:

Once “Memory phase” is done, it’s time for “Execution Phase”. In this phase JS engine executes the code line by line.

Here the first line is console.log("Value of a is: ", a); . So this will be executed first. But the variable in the memory still does not hold the value. It holds “undefined” as the value yet. So when the code line is executed it takes undefined as a value.

And this is why you get undefined when you print your variable before declaring it with var.

BTW the GEC is not over yet. You know why? Because the there is still a line of code that has not ben executed yet.

So the next line var a = 12; will be executed. Since the value has already been hoisted and initialized in the memory phase. So now only the value will be updated.

And now the GEC will be deleted as it’s task is completed.

Example-3:

a = 50;
console.log("The value of a1 is", a); 
var a = 20;
console.log("The value of a2 is",a); 

// Output:
// The value of a1 is 50
// The value of a2 is 20

I guess most of you already have understood how this is executed right? If not then here’s the task

TASK: Go and try to understand / visualize how this code might work. Make diagram on paper or somewhere. Execute each line one by one. Once finished come here and check if you were right or not.

Memory Creation Phase:

As you know, in this phase variable is hoisted and initialized with the value undefined . (Hope there is no need to show the diagram again)

Execution Phase:

In this phase the code s executed line by line.

line-1: The first line is a = 50; So the value of variable a in memory is changed from undefined to 50.

line-2: The second line is console.log("The value of a1 is", a); Since the value of ais 50. It will print the value of a as 50.

line-3: var a = 20; Since the variable is already hoisted and initialized, var keyword is ignored. This line simply updates the value of a from 50 to 20.

line-4: console.log("The value of a2 is", a); When this line is executed it prints the line and the value of a which is updated to 20.

Have you completed your task? Were you right?

What about let and const?

You must be thinking, “Hey Shouvik, it’s cool to know what happens when we initialize a variable with var, but in reality we don’t use var anymore. We use let and const so what about them?”.

Well, I know I know, and thanks for holding your horses this long, now you are about to know what happens when we use let and const .

But before that you’ll have to understand one concept and that is Temporal Dead Zone.

Temporal Dead Zone:

In JavaScript, the Temporal Dead Zone (TDZ) refers to the time period between the hoisting of variables declared with let or const and their actual initialization. During this time, the variables exist in memory but cannot be accessed, and any attempt to do so results in a ReferenceError.

The TDZ occurs within the block scope where the variable is declared. So while variables are hoisted to the top of the block, they remain in an uninitialized “dead zone” until their declaration line is executed.

I know you are like “What the hell does that mean Dude?”

Yeah I know that was very hard to understand. Let me explain you properly.

When you declare a variable using let or const, it is hoisted during the Memory Phase of the Global Execution Context (GEC), but it is not initialized with undefined (unlike var). Instead, it enters a special state called the Temporal Dead Zone (TDZ), where the variable is inaccessible.

The variable exits the TDZ once the line where the variable is initialized gets executed in the Execution Phase. Accessing the variable in this zone will result in a ReferenceError.

NOTE: const and let work the same way( in this scenario ).

Example-1

console.log("Value of a is", a);
let a = 12;

// Output: ReferenceError: Cannot access 'a' before initialization

So What actually happened here?

Memory Creation Phase:

In memory creation Phase the variable was hoisted and was allocated in the memory but instead of getting initialized with undefined it entered TDZ.

Execution Phase:

In execution phase as you already know JS Engine executes the code line by line.

line-1: console.log("Value of a is", a); While executing this line JS Engine tries to access the value from memory but it fails to do since the variable is in TDZ. And finally it returns a Reference Error .

Since the 2nd line let a = 12; was never reached (because the first line threw a ReferenceError), so in memory a was never initialized with the value.

Example-2:

let a = 12;
console.log("Value of a is", a);

// Output: Value of a is 12

In this example since the value is printed after initialization, we get the value and not error.

How?

Memory Creation Phase:

All the values of the code are hoisted but not initialized. And also in the TDZ waiting to be initialized

Execution Phase:

Each line of the code is executed one by one.

line1: The first line is let a = 12; So now finally a will be initialized with a value 12.

line2: The second line is console.log("Value of a is", a);. Since the variable a is initialized with 12. It prints the value of a which is 12;

Example-3:

a = 20;
console.log("Value of a is", a);
let a = 12;

// Output: ReferenceError: Cannot access 'a' before initialization

Well you may ask why do we get error in this scenario?

The reason is very simple. Let me explain,

Memory Creation Phase:

All the values of the code are hoisted but not initialized. And also in the TDZ waiting to be initialized

Execution Phase:

Each line of the code is executed one by one.

line1: The first line is a = 20; It is assigning a value in a before a is initialized. Since a is not initialized yet and still in TDZ.

Since the first line failed to get executed and returned an error, rest of the code will not be executed.

Happy learning:

Hopefully you learnt something new today and gained a deeper concept about how JS works behind the scene.

  • Don’t just read the article practice yourself

  • Try different examples

  • Draw diagrams and try to explain.

  • Write an article yourself or teach online(or a friend).

Do let me know if you did all those or few of them. I’ll be grateful.

Here are my socials:

Happy Coding Happy Learning.

51
Subscribe to my newsletter

Read articles from shouvik sarkar directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

shouvik sarkar
shouvik sarkar