No history yet

Execution Contexts

The Execution Context

Every line of JavaScript you write runs inside an environment called an Execution Context. Think of it as a wrapper for your code, containing everything needed for it to be executed. When your script first runs, the JavaScript engine creates a Global Execution Context. This is the base level, the default environment for any code that isn't inside a function.

Every line of JavaScript runs inside an Execution Context — this is the foundation of how JS works.

When a function is called, a new, separate environment is created just for that function. This is a Function Execution Context. Since you can have many functions, and functions can call other functions, you can have many Function Execution Contexts. The engine needs a way to manage all of them.

The Two Phases of Execution

Before the JavaScript engine executes your code line-by-line, it first does a quick scan. This process happens in two distinct phases for every execution context.

  1. Memory Creation Phase: The engine allocates memory for all variables and functions defined within the context. For variables declared with var, it assigns a placeholder value of undefined. For function declarations, it stores the entire function code in memory. This is why you can call a function before its physical declaration in the code, a behaviour known as hoisting.

  2. Code Execution Phase: After setting up memory, the engine executes the code line by line. It assigns the actual values to variables and runs the logic inside functions.

Let's look at a simple example:

console.log(myVar); // logs 'undefined'
var myVar = 10;
console.log(myVar); // logs 10

myFunction(); // logs "Hello!"

function myFunction() {
  console.log("Hello!");
}

In the Memory Creation Phase for the Global Execution Context:

  • Memory is allocated for myVar and it's initialized to undefined.
  • Memory is allocated for myFunction, and the entire function's code is stored.

In the Code Execution Phase:

  1. console.log(myVar) runs. The engine finds myVar in memory and sees its value is undefined.
  2. The line myVar = 10 is executed, and the value of myVar in memory is updated to 10.
  3. console.log(myVar) runs again, this time logging 10.
  4. myFunction() is called. The engine finds it in memory and executes its code.

The Call Stack

So how does the JavaScript engine keep track of which execution context is currently running, especially when functions call other functions? It uses a mechanism called the Call Stack.

The Call Stack is a data structure that operates on the Last-In, First-Out (LIFO) principle. The last context that gets pushed onto the stack is the first one to be popped off.

Here’s how it works:

  1. When the script starts, the Global Execution Context is created and pushed onto the call stack.
  2. If a function first() is called, a new Function Execution Context for first() is created and pushed on top of the Global context.
  3. If first() then calls another function, second(), an execution context for second() is created and pushed on top of first()'s context.
  4. The engine always executes the context at the top of the stack. So, it runs the code in second().
  5. Once second() finishes, its context is popped off the stack. Control returns to first()'s context, which is now at the top.
  6. When first() finishes, it too is popped off. Control returns to the Global Execution Context.
  7. When the entire script is finished, the Global Execution Context is popped off, and the stack is empty.

This orderly, single-file process is what makes JavaScript a single-threaded language. It can only execute one piece of code at a time, corresponding to the single execution context at the top of its one and only call stack.

Quiz Questions 1/5

What is the very first execution context created when a JavaScript script begins to run?

Quiz Questions 2/5

An execution context is created in two phases. What happens during the first phase, the Memory Creation Phase?

Understanding the execution context and the call stack is fundamental. It clarifies how the JavaScript engine interprets and runs your code, explaining concepts like hoisting and the single-threaded nature of the language from the ground up.