JavaScript Execution Context: A Complete Tutorial
An execution context is the environment JavaScript creates every time it runs code. Learn how global and function contexts, scope chains, and the creation phase work.
An execution context is the environment that JavaScript creates every time it runs your code. It holds the variables, functions, and scope rules that a piece of code can access. Whenever a script starts or a function is called, the engine builds a new execution context.
Think of an execution context as the workspace for a piece of running code. It contains everything the code needs: its own variables, a reference to outer scopes, and the value of the this keyword.
Two Kinds of Execution Context
JavaScript creates two types of execution context. The global context is created once when the script first runs and stays alive until the program ends. Function contexts are created every time a function is called and destroyed when the function returns.
The global context is always at the bottom of the call stack. Every function call pushes a new context on top. When the function returns, its context is destroyed and the previous context resumes.
What an Execution Context Contains
Every execution context has three key parts.
Variable Environment
The variable environment stores all local variables and function declarations for that scope. When you declare let count = 0 inside a function, count lives in that function's variable environment. Variables declared with var are stored one way, while let and const are stored separately in the lexical environment, but the concept is the same: the context holds your local variables.
Scope Chain
Every context has a reference to its outer, parent environment. When the engine looks up a variable and cannot find it in the current context, it follows this reference outward. It keeps walking up the chain until it reaches the global context.
This chain of references is the scope chain, and it is what makes closures possible.
The This Binding
Each context sets the value of the this keyword. In the global context, this is the global object (window in browsers, global in Node.js). In a function context, the value of this depends on how the function is called.
The Two Phases: Creation and Execution
Every execution context goes through two distinct phases. This is the key to understanding hoisting and scope behavior.
The creation phase happens before any of your code runs inside that context. The execution phase is when your actual code runs, line by line.
Creation Phase
Before running a single line, the engine does three things. First, it sets up the scope chain by creating a reference to the outer environment. Second, it creates the variable environment by scanning for variable and function declarations and allocating memory.
Third, it determines what this points to.
During this phase, var variables are initialized to undefined. let and const variables are created but remain uninitialized.
Function declarations are stored in full. This is why you can call a function before its declaration.
Execution Phase
Now your code runs, line by line. Variables get their actual values. Function calls trigger new execution contexts.
Expressions are evaluated. The engine follows the scope chain for every variable lookup.
How the Creation Phase Explains Hoisting
Hoisting is not a feature you enable. It is a side effect of the creation phase.
console.log(name);
console.log(age);
var name = "Alex";
let age = 30;The creation phase scans the code, finds var name and let age, and allocates memory for both. The variable name gets initialized to undefined. The variable age is created but stays uninitialized, in what the spec calls the Temporal Dead Zone.
During the execution phase, logging name prints undefined because the variable exists, just with no real value yet. Logging age throws a ReferenceError because the variable exists but is uninitialized and cannot be accessed.
Function declarations are fully hoisted. You can call a function before its declaration line because the creation phase stores the entire function definition. For more on how variables behave across scopes, see /javascript/global-vs-local-variables-in-javascript-guide.
The Global Execution Context in Detail
When your script starts, the engine creates the global execution context. This context holds all top-level variable declarations, creates the global object (window in browsers), sets this to the global object, and has no outer environment because the scope chain ends here. The global context runs through its creation phase, then its execution phase, and stays alive until the tab closes or the Node.js process exits.
Function Execution Contexts in Detail
Every function call creates a new execution context. Even the same function called twice creates two separate, independent contexts.
function multiply(a, b) {
const result = a * b;
return result;
}
multiply(3, 4);
multiply(5, 2);The first call to multiply creates a context with its own a, b, and result. The second call creates a completely separate context. The two contexts do not interfere with each other.
Each gets its own variable environment with its own parameter values.
In non-arrow functions, the creation phase also sets up an arguments object, an array-like structure containing all passed arguments. This is available even when the function declares no named parameters. Arrow functions do not get their own arguments object; they inherit it from their enclosing scope.
How Contexts Interact with the Call Stack
Execution contexts and the call stack work together. The call stack tracks which context is currently active.
Here is code that creates this pattern:
function inner() {
console.log("Inside inner");
}
function outer() {
console.log("Inside outer");
inner();
console.log("Back inside outer");
}
outer();
console.log("Back in global");This example demonstrates three levels of context nesting. The global context runs outer, outer runs inner, and each return unwinds the stack in reverse order.
The output confirms this exact order of context creation and destruction.
Inside outer
Inside inner
Back inside outer
Back in globalThe global context runs first. Calling outer pushes a function context. Calling inner pushes another.
When inner returns, its context is popped and destroyed. Then outer returns and its context is destroyed. The global context is now on top again.
For more on how the call stack manages this process, read /javascript/how-the-js-call-stack-handles-function-execution.
Execution Context and Closures
A closure is created when a function retains access to variables from an outer execution context, even after that outer context has been destroyed.
function createCounter() {
let count = 0;
return function increment() {
count += 1;
return count;
};
}
const counter = createCounter();
console.log(counter());
console.log(counter());When createCounter returns, its execution context is removed from the call stack. But the returned increment function still holds a reference to count through the scope chain. That reference is the closure.
The variable count stays alive because something still needs it.
For a full explanation of closures, see /javascript/javascript-closures-deep-dive-complete-guide.
The Eval and Block Contexts
In strict mode, let and const declarations inside a block also get their own lexical environment, sometimes called a block-level context. The eval function also creates its own execution context with its own variable environment, which is one reason eval is both powerful and dangerous.
Rune AI
Key Insights
- An execution context is the environment holding variables, scope chain, and the this keyword for running code.
- The global context is created once. Function contexts are created for every call and destroyed on return.
- The creation phase allocates memory for variables and functions before any code runs.
- var variables initialize to undefined. let and const are created but stay uninitialized in the Temporal Dead Zone.
- Each context has a reference to its outer environment, forming the scope chain.
Frequently Asked Questions
How many execution contexts can exist at once?
What is the difference between global and function execution context?
Conclusion
Every line of JavaScript runs inside an execution context. The global context starts the program. Every function call creates a new context with its own variable environment, scope chain, and this binding. The creation phase sets up variables and functions before code runs, which is why hoisting works the way it does. Understanding contexts makes scope, closures, and the call stack much easier to reason about.| Phase | What Happens | |---|---| | Context creation | Engine sets up scope chain, variable environment, and the this keyword | | Creation phase | Allocates memory for variables and functions; var gets undefined, let and const are uninitialized | | Execution phase | Runs code line by line; assigns values; calls functions | | Function call | Current context pauses; new context is created and pushed onto the stack | | Function return | Current context is popped off the stack and destroyed; previous context resumes | The lifecycle is mechanical and predictable. Every function call creates a context. Every return destroys one.
More in this topic
Using Decorators for Logging in JS Architecture
Learn how JavaScript decorators wrap class methods to add logging without touching the original code, including setup with Babel and how execution order works.
JavaScript While Loop Explained: A Complete Guide
A while loop repeats code as long as a condition stays true. Learn the syntax, see practical examples, and understand when a while loop is the right choice over a for loop.
JavaScript Loops Tutorial: for, while, do while
Loops let JavaScript repeat code without writing it twice. Learn the three core loop types, what each one does, and when to choose one over another.