Why do we care about the Execution Context?
What is Execution Context?
Before looking at execution context in action, let's first define what execution context is.
Execution context is a wrapper containing information about the code currently being evaluated and executed. In other words, it's the environment in which the code is executed.
Types of Execution context
- Global Execution Context
- Function Execution Context
This post will cover the Global Execution Context in detail as the knowledge of the Global Execution Context lays the groundwork for understanding Function execution context and the need for it.
From here on, when we mention Execution Context, we will be referring to the Global Execution Context.
Global Execution Context
When the Global execution context is created, we get access to a few things; the Global Object and 'this'. Let's look at the Global Object and 'this' in practice.
Open the HTML file in a browser and open up the console in the dev tools.
Type window in the console
- Let's now look at the keyword 'this'. Type this in the console
Notice, 'this' is the same as the browser's Global Object - window, i.e. 'this' is a reference to the window object.
So, anytime the Global execution context is created, a global object (window, in the case of the browser) is created, and its reference is passed to 'this'.
You can look at the Node's global object by opening a Node REPL and typing 'this'
Phases of Execution Context
Phase 1: Creation Phase
- The global object is created in the creation phase (We already looked at the global object).
- 'this' is created, and it is bound to the global object.
- The memory heap is set up, and variables, and function declarations are stored inside the memory heap (We will come back and look at this in detail).
Phase 2: Execution Phase
- Code is executed line by line:
- Values are assigned to variables
- Functions are invoked (or called).
Creation phase and Execution phase in practice
Variable int is declared with var intentionally. We will look at ES6's let and const later.
Let's save and run our file, and see what we get in the console.
We get the expected output -
- The value of variable int is printed to the console.
- The log statement inside the loggerFunction is printed to the console.
Now, let's change our code a bit.
We made the following changes -
- We are logging the variable int to the console before it is declared and initialized.
- We are invoking the loggerFunction before it is declared.
Before running the code, let's understand this code inside the phases of execution code.
We already know that in the creation phase, a memory heap is set up within which variables, and function declarations are stored.
Creation Phase in our code,
- window object is created, and its reference is set to 'this'
- variable int and loggerFunction declarations are stored inside the memory heap.
In the execution phase, the code is executed line by line. In this phase, values are assigned to variables and functions are invoked.
Execution Phase in our code,
- The variable int is logged to the console (NOTE: int has not yet been assigned any value)
- Once the int is logged to the console, the next line assigns value to the variable int.
- loggerFunction is invoked.
Let's run our code and look at the output in the console:
Let's understand what happened here -
The value of variable int is undefined: We did not get an error for logging the variable int to the console before it was declared and initialized because during the creation phase (before even a single line is executed), the declaration of variable int was stored in the memory.
The variable int is set to 'undefined' because it has not yet been assigned any value (assignment happens in the execution phase).
The loggerFunction is invoked before it is declared: We were able to invoke the loggerFunction before it was declared because, in the creation phase, function declarations are stored in the memory, so, we had access to the loggerFunction declaration when it was invoked.
The behaviour we saw with our variable and function declaration is what we call Hoisting.
Let and Const are not Hoisited.
Variables declared with let and const are not hoisted.
Let's declare our variable int with let and see the output.
When we run the code we get the error:
As mentioned above, this is because let and const are not hoisted. We will learn about Hoisting in detail in another post.
When a program is written for multiple environments, it becomes complicated to consistently refer to the global object of an environment.
This was solved with the introduction of globalThis in ES2020. You can learn more about globalThis here.