How does JavaScript work internally?

Rishabhagarwaljhs
Technogise
Published in
5 min readNov 24, 2021

When we run a JavaScript program, do we ever think how it runs internally? Let us understand the internal workings of a JavaScript program.

Everything in JavaScript happens inside an execution context.
So, what is an execution context ?

We can imagine execution context as a big box with two components in it:

  1. Memory component / Variable environment: This is the place where memory is allocated to all the variables and functions.
  2. Code component / Thread of execution: This is the place where code execution takes place synchronously.

Let’s understand it better by taking an example.

Consider the below code as a test code:

When we execute this code, the execution context is created. The creation of execution context is a two step process.

Memory Allocation

In the first step, JavaScript will allocate memory to all variables and functions. It stores all variables a, b, output with a special value which is undefined . In case of functions, it stores the whole code attached to the function name.

Memory allocated to variables and function

Code Execution

Now comes the code execution step. JavaScript runs the code synchronously. Here it sees that variable a is assigned a value 10 and similarly variable b is assigned with value 20, so it updates the respective variables with their values. Next it moves to line 7, where it finds a function (declaration / statement) that has already been allocated with memory during step 1, so it moves ahead to line 11.

Now on line number 11, it invokes the function aFunctionToCall with values of a, b as arguments. Whenever a function invocation takes place, a new execution context is created. Now the same process happens with the new execution context, i.e memory allocation followed by code execution.

  1. Memory is allocated for variables x, y, sum and are assigned with undefined.
  2. Code execution — Variables x, y are assigned with values 10, 20 received as parameters. Sum of x and y is calculated and assigned to a variable sum which is then returned.
Code execution of aFunctionToCall

Once the code inside the function is completely executed and it encounters a return statement, the control is sent back to the parent execution context which had called it and the child execution context (AFunctionToCall in the image above) pops out and gets deleted from the call stack*.

Now the control is back to line 11 and the returned value is assigned to variable output. Once the whole code is executed, the global execution context, which is the very first execution context, also gets popped out and the call stack is empty.

Entire code execution flow

* You must be wondering what a call stack is ?
A call stack is a stack which helps in executing JavaScript code. It is a stack with global execution context at the bottom.
If a new function is invoked, its execution context is pushed into the stack and once the scope of the function is finished, it pops out and gets deleted from the call stack. Once the completed program is executed, the global execution context also pops out and gets deleted, making the call stack empty.

Now let’s take another example with closures to understand how execution context works.

Memory allocation: As discussed above, in this phase JavaScript will allocate memory to all variables and functions. Variables aNumber, aVariableMultiplyBy2, aOutput get assigned to undefined while memory is allocated to function aFunctionMultiply with its whole code attached to it.

Code Execution: Now comes the code execution phase. JavaScript runs the code. At line 5, variable aNumber is assigned with value 2. Next it moves to line 6 where it will see a function (declaration / statement), so it moves ahead to line 11 where a function aFunctionMultiply is invoked with argument aNumber with value 2.

Once JavaScript reaches line 7, the execution context of this function gets created. Variable x is assigned with value 2 and the function along with its lexical scope this gets returned.

We can see in the above code snapshot that the execution context for function aFunctionMultiply is created and has variable x assigned with 2. Line 7 has a return statement so function aFunctionReturned along with its lexical scope this is returned ( which forms a closure).

You must be thinking what is Lexical scope
Lexical scope means that in a nested group of functions, the inner functions have access to the variables and other resources of their parent scope. This means that the child’s functions are lexically bound to the execution context of their parents.

Now JavaScript comes back to line 11, where the whole function that was returned is stored in variable aMultiplyBy2 and execution context for function aFunctionMultiply pops out and gets deleted as discussed earlier.

JavaScript moves to line 12 where variable aMultiplyBy2 which has a function definition gets invoked. Execution context for it gets created.

You must be wondering how aFunctionReturned will access value x inside execution context of aMultiplyBy2 .

aMultiplyBy2 forms closure with aFunctionMultiply.

As we know when a function is returned, function along with its lexical scope gets returned. When a function cannot find any variable inside its scope, it searches its parent’s scope for the same.

As we know, variable aMultiplyBy2 has function definition of aFunctionReturned stored in it. Because of closure, it gets value of x from aFunctionMultiply and multiplication is done.

--

--