How functions are executed in JS

shouvik sarkarshouvik sarkar
7 min read

Hey JS champions, welcome back!

I hope you've already gone through Part 1 of How JS Actually Works.

If you have, awesome! Let’s dive deeper into functions.

We're going to explore how JavaScript treats different kinds of functions, and how their behavior changes depending on how and where you declare them.

So, are you ready?

Function

When you define a function using a function declaration, you can call it before or after the declaration appears in your code because of hoisting:

greet();      // Output: Hello

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

greet();       // Output: Hello

But for function expressions and arrow functions, it doesn’t work the same way:

const greet = function(){
    console.log("Hello")
}

greet();    // Output: Hello
const greet = () => {
    console.log("Hello")
}

greet();    // Output: Hello

But when we call the function before:

greet();

const greet = function(){
    console.log("Hello")
}

// Output: ReferenceError: Cannot access 'myName' before initialization
greet();

const greet = () =>{
    console.log("Hello")
}

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

Do you know why function behaves this ways? If not let’s learn and try to understand how function is treated in JS.

NOTE: This behavior is the same in Node.js and the browser, but in the browser console, it may seem to behave differently due to how DevTools evaluates your code line-by-line.

Let’s try to understand them one by one.

But before that you need to learn one about Local Execution Context.

Local Execution Phase:

When a function is called, a Local Execution Context (LEC) is created inside the Execution Phase of the Global Execution Context (GEC).

The structure of the LEC is the same as the GEC. It also has two main phases:

  1. Memory Creation Phase (or Creation Phase)

  2. Execution Phase

These phases work the same way as in the GEC:

  • In the Memory Phase, variables and function declarations within the function are stored in memory (with undefined for var variables).

  • In the Execution Phase, the code inside the function runs line by line, and values are assigned.

The LEC is specific to the function scope, and once the function finishes execution, its execution context is popped off the call stack and removed from memory.

here is the diagram to understand better:

Example-1:

console.log("Program Starts")

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

greet();       // Output: Hello

console.log("Program Ends")

Memory Creation Phase:

As you know initially a GEC will be created. Since there’s no variables but only function, in “Memory Creation Phase” the function with it’s function body stored in the memory.

Execution Phase:

In this phase the JS engine will scan the code line by line and execute each of the code line.

line-1:

First line here is console.log("Program Starts") and it gets executed first and print the value "Program Starts".

Line-2:

Now the second line is not actually just a line, it is the function scope it self. Since function is already hoisted with the function body, it gets ignored.

Line-3:

Next line is calling the function. When the function is called a Local Execution Context (LEC) is created inside GEC execution phase.

What happens inside LEC? Well LEC works almost same as GEC. Memory Creation Phase will hoists the variables and function and then the Execution Phase executes the code.

As you can see The line is executed and after this LEC will be deleted. And rest of the code lines will be executed in Execution Phase.

Line-4:

The last line is console.log("Program Ends") and it finally gets executed. And the value is printed and later the GEC is deleted.

Example-2:

function greet(){
    const name = "Piyush";
    console.log("Hello", name)
}

greet();       // Output: Hello Piyush

In this example you have a variable inside the function scope. Let’s see how it works.

Memory Creation Phase:

Since in this code there’s no variable to hoist, the function gets hoisted.

Execution Phase:

In Execution Phase when the function is called an LEC is created.

As you know LEC works same as GEC. So in this LEC.

So here in the Memory Creation Phase of this LEC . The variable name is hoisted

Inside the Local Execution Context:

Inside the Memory Creation Phase variable b is hoisted but not initialized (since declared by const). So name is placed in memory but remains in the Temporal Dead Zone until the actual line of declaration.

Then inside the Execution Phase the the code lines inside the function scope get executed.

The first line allocates the value inside the hoisted variable. And then the 2nd line gets executed.

Once the code lines are executed, LEC get deleted and control returns to the GEC.

Since there are no more statements, the GEC also ends and the program terminates.(In this case)

Why can we call functions before?:

In JavaScript, function declarations are hoisted along with their entire function body. Unlike variables declared with let or const, functions don’t enter the Temporal Dead Zone (TDZ).

That’s why when you call a function before its actual declaration in the code, JavaScript already knows about it, thanks to the Memory Creation Phase.

So, when the Execution Phase reaches the function call, the function (along with its body) is already in memory and accessible.

You might be thinking “Then What’s wrong with Function Expression and Arrow Function?”

Well when we declare a function expression

greet();

const greet = function(){
    console.log("Hello")
}

// Output: Reference Error: Cannot access 'greet' before initialization

Or declare Arrow Function:

greet();

const greet = () => {
    console.log("Hello")
}

// Output: Reference Error: Cannot access 'greet' before initialization

When try to call these functions, why do we get errors?

The reason is very simple when we store a function inside a variable then it’s treated as a variable in the Memory Creation Phase. A variable which is hoisted but not initialized and in the TDZ. So basically inaccessible.

That’s why when we try to call Arrow function or Function Expression, It gives us that error.

Example-3:

function greet(name){
    console.log("Hello", name)
};
greet("Hitesh")

When we call this function. An LEC is created. (I am not repeating the GEC part since you are already experts😎)

step-1:

name parameter is hoisted. But unlike other variables it gets initialized with the value we passed as argument while calling. It doesn’t get initialized by undefined or does not enter TDZ.

step-2:

Since name parameter is already initialized. When the execution Phase executes the code line console.log("Hello", name the name variable gets it’s value from memory.

Once everything is done the LEC gets deleted and the control returns to GEC.

What about return statement?

Well we all know in function we can return a value. But in any of our previous examples we did not use any return statements. So what happens when we do and do not use return statement?

When we use:

function greet(){
    return "hello"
}

const a = greet();
console.log(a);
// output: hello
  • When we return a value, first of all it exists the function and LEC gets deleted

  • Any code line written after return statement does not get executed and get ignored.

  • The function returns it’s value where it was called. greet() in this scenario.

  • If we store the function call in a variable then the value is stored in that variable. And gets printed once the variable is printed.

If we don’t return explicitly:

function greet(){
    console.log("hello");
}
const a = greet();
console.log(a);
// output: hello
//    undefined

When we don’t return anything explicitly in a function, Then the function returns an undefined itself.

This undefined is also returned to where the function was called.

And if we store assign that to a variable and print it we will get undefined as a result.

Interesting thing to know

function greet(){
    console.log("hello");
}
greet();

If you write this piece of code in your browser you’ll get hello as result as expected but also get an undefined as a 2nd result. This undefined is the the value returned by function because you did not return a value explicitly.

Conclusion:

JavaScript handles functions a bit differently than regular variables, and it really depends on how you define them. Still, functions are a core part of the language and honestly, pretty interesting to dig into. Once you understand how they run behind the scenes, it gives you a whole new way to think about how JavaScript works.

Happy learning:

Hopefully you learnt something new today and gained a deeper concept about how JS functions 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