What is this place. This that JavaScript creates to run code. This that is destroyed after there are no more tasks to do. No more lines to read no more instrucctions to follow.
It is called Execution Context and every time we call a function JavaScript creates a new one to run that function's code.
Surprised? Probably not. Yes, the Global Execution Context is not the only one out there.
Welcome to the Execution ContextMultiverse.
A new Dimension
An Execution Context is the environment JavaScript uses to evaluate and execute a specific piece of code.
JavaScript creates a new Execution Context when it runs:
Global code — This is the environment your program starts in. Any code that is not inside a function or a module runs here. We call it the Global Execution Context.
Function code — Every time JavaScript enters a function body, it switches into a new environment that belongs exclusively to that function.
Others not relevant for us right now.
So let’s see how it works through an example.
But first, a few things to improve our technical language.
We want to be precise when talking about what is happening in our code, and that’s why we need to clarify these terms.
In a function, we call:
Parameter — the placeholder or label that receives the value.
Argument — the actual value passed to the function.
Local Memory — also calledVariable EnvironmentorState. It is the storage area of the Execution Context.
Inside an Execution Context, JavaScript keeps track of variables and declarations using internal structures called the Lexical Environment and the Variable Environment.
But we are going to refere to these terms as the Local Memory of that execution context.
Shrek's surname
Now we are ready to read this code like if we were JavaScript:
const name = 'Shrek';function addSurname(inputName) { const result = inputName + ' Evergreen'; return result;}const ogre = addSurname(name);
This diagram is a graphic representation of the steps 1 to 5.
JavaScript creates the Global Execution Context, and the Thread of Executionstarts evaluating the code.
What does “evaluate” mean?
When we say that JavaScript evaluates code, we simply mean that JavaScript reads an expression, processes it, and produces a result.
For example:
2 + 3;
This expression evaluates to 5.
This small idea applies to more complex code:
createTreasure();
JavaScript evaluates this expression by:
Finding the function createTreasure.
Running its code.
And producing a final value (whatever the function returns).
So when we say:
The Thread of Execution starts evaluating the code
We mean that JavaScript starts reading the code line by line, resolving identifiers, executing expressions, and producing values as it goes.
The identifier name is declared and initialized with the string value 'Shrek'.
This one is tricky. The reserved word function is read, and the function block is saved as the value of the identifier addSurname. In other words, the identifier addSurname now has the function definition as its value.
When we say that the function definition is saved as the value of addSurname, we mean that the function’s code block is literally stored as a value and won’t be read or executed until the function is invoked with parentheses ().
{ const result = inputName + ' Evergreen'; return result;}
The identifier ogre is declared but not yet initialized. JavaScript needs to evaluate the expression addSurname(name) to determine it's value. Since parentheses follow addSurname, JavaScript knows the function must be invoked, so it creates a new Execution Context for that function call. Before entering the function, the identifier name is evaluated to the string 'Shrek', and this value is passed as an argument to the addSurname Execution Context.
Inside the new Execution Context, the argument 'Shrek' is assigned to the parameter inputName. This binding is stored in the Local Memory (it's lexical environment).
This diagram is a graphic representation of the steps 6 to 11.
The Thread of Execution now starts evaluating the body of addSurname inside this new Execution Context.
After evaluating inputName + ' Evergreen', the identifier result it's initialized in the Local Memory with the string value of 'Shrek Evergreen'.
The return result statement sends the value 'Shrek Evergreen' back to the calling context.
Remember that an Execution Context is destroyed after the function finishes running, which means that all the values in its Local Memory are destroyed too unless we return them.
What makes a function stop running? When it encounters a return statement or reaches the last closing bracket }, meaning it has reached the end of the function code.
The Thread of Execution resumes in the Global Execution Context at the exact point where it left off before entering the addSurname Execution Context.
The identifier ogre is initialized in the Global Memory with the returned value 'Shrek Evergreen', which is the result of evaluating the function call addSurname(name).
Since there is no more code to evaluate, the Global Execution Context is destroyed and the program ends.
Good job! We’re making good progress. We’ve just learned how the Execution Context works in JavaScript: how it’s created, how it’s destroyed, and how it’s executed.
We’ve seen how JavaScript runs code inside the Global Execution Context and how it creates a new Execution Context to run function code. We’ve also seen how the Thread of Execution goes back to where it was to continue running global code after a function finishes running.
We’ve walked through a key part of our journey.
Keep it up.
If there are still some gaps in this section, don’t hesitate to go back and re-read it calmly. We can wait — we have time.
Are you ready to continue our journey? Let's go.
There is something I want to talk you about. It's a way to track in which Place we are during the execution of our program. It's pretty much like a compass. Take a look.
Compass
Wouldn’t it be nice, if we lost our way in the mountains, to be able to find the road again? Or at least to know where we are or how we got there?
Well, JavaScript developers probably had the same feeling back in the day, and this might be the reason why they created the Call Stack:
A Multiverse Compass that tells you the Place where the Servant is working and the way to get there.
In technical words
The Call Stack tells us the Execution Context (Place) where the Thread of Execution (Servant) is currently working on.
Example:
function secondToRun() {}function firstToRun() { secondToRun();}firstToRun();
Let me explain you how it works:
When a function is invoked it is pushed (added) to the call stack.
When the function finishes running it is popped (removed) from the call stack.
When no more functions are found in the Call Stack, execution returns to global(). The Global Execution Context is always at the bottom of the Call Stack while the program is running, since it is the first Execution Context created.
If there are multiple functions in the call stack, how do we know which one the Thread of Execution is currently working on?
Whatever is at the top of the call stack - that is the currently running function.
If you see multiple functions in the Call Stack, it means that the top function was invoked by the function below it, and this process repeats sequentially until execution returns to global() at the bottom.
Sharpen your mind
We often say that a function is pushed to or popped from the Call Stack, but what actually happens is that the Execution Context created to run that function’s code is pushed or popped from the Call Stack.
I just wanted to clarify this to avoid confusion, since it’s common to hear both expressions and they refer to the same thing.
Use your compass
Let's go through this code, It's a simplified version of code so we can focus only on the important parts. You can imagine that /* some fancy code to run */ represents real logic inside the function.
function firstToRun(callback) { /* some fancy code to run*/ callback(); /* some fancy code to run*/}function callMeLater() { /* some cooler code to run*/}firstToRun(callMeLater);
The Global Execution Context is created, and the Thread of Execution starts evaluating the code.
The identifier firstToRun is found and saved in Global Memory with the function definition as its value.
The identifier callMeLater is found and saved in Global Memory with the function definition as its value.
The identifier firstToRun is searched in memory and found (JavaScript saved it in step 2). Since the expression firstToRun(callMeLater) has parentheses, JavaScript knows it is a command and will execute the function instead of returning the function definition stored in firstToRun.
A new Execution Context is created to run the code inside firstToRun.
The Execution Context of firstToRun is pushed onto the Call Stack.
The function definition stored in the identifier callMeLater is passed as an argument to the Execution Context of firstToRun and saved as a parameter with the label callback in the Local Memory.
The /* fancy code */ of firstToRun is sequentially execute until it reaches callback().
The identifier callback is searched in Local Memory and found (JavaScript saved it in step 7). Since the expression callback() has parentheses, JavaScript knows it is a command and will execute the function instead of returning the function definition stored in callback.
A new Execution Context is created to run the code inside callback.
The Execution Context of callback is pushed onto the Call Stack.
The /* cooler code */ of callback is sequentially execute until it reaches the end of the function.
Since there is no more code to evaluate the Execution Context of callback is destroyed.
The Execution Context of callback is popped from the Call Stack.
The Thread of Execution goes back to the firstToRun Execution Context and starts running the /* fancy code */ below the callback() until it reaches the end of the function.
Since there is no more code to evaluate, the Execution Context of firstToRun is destroyed.
The Execution Context of firstToRun is popped from the Call Stack.
The Thread of Execution move again to the Global Execution Context, and since there is no more code to run, the Global Execution Context is destroyed and the program ends.
We can take a lot from this example.
Even in such a small example, the Call Stack reveals how JavaScript truly runs our code:
From this, we learn that the Call Stack keeps track of which Execution Context is currently active.
This leads to an important consequence: functions don’t run inside each other, they take turns. Each function call creates a new Execution Context, gets pushed onto the stack, and must finish before the previous one can continue.
With this in mind, we’re ready to take a small detour and explore our first mini bonus episode.
If you’re wondering what Higher Order Functions are and how they’re used in practice, you’ll enjoy this optional stop.
If not, here’s the only thing you need to remember before moving on.
Functions are just values. That means you can pass them as arguments to other functions, or return them from functions.
Nice. Take a breath. We’re making good progress.
Now it’s time to pause for a moment and introduce the next topic — a very important one: Lexical Scope. It will teach us a key skill: how to identify which variables are accessible in a given part of our code.
Let’s find out in the next episode.
Recap
The Execution Context is the enviroment where code is being run.
Every time a function is invoked, a new Execution Context is created to run that function’s code.
The Call Stack keeps track of the Execution Contexts created during execution and tells us which one is currently active.
When a function finishes running, the Thread of Execution resumes exactly where it left off in the calling context.
A function finishes running when it encounters a return statement or reaches the end of its body }.
Functions are just values. That means you can pass them as arguments to other functions, or return them from functions.