JavaScript Execution Context - Complete Guide
Table of Contentsโ
- What is an Execution Context?
- Types of Execution Context
- Global Execution Context (GEC)
- Function Execution Context (FEC)
- Call Stack
- The
thisKeyword in Execution Context - Lexical vs Variable Environment
- Hoisting Summary
- Complete Execution Flow Example
- Execution Context vs Scope
- Closures and Execution Context
- Execution Context and Event Loop
- Microtasks and Execution Context
- Common Interview Questions
- Mental Models
What is an Execution Context?โ
An Execution Context (EC) is the environment where JavaScript code is evaluated and executed. Think of it as a container that holds all the information needed to run a piece of code.
Every Execution Context has three components:
- Variable Environment - stores variables declared with
var - Lexical Environment - stores variables declared with
letandconst thisbinding - determines whatthisrefers to
Types of Execution Contextโ
There are three types of execution contexts in JavaScript:
- Global Execution Context (GEC) - created when the JavaScript file starts executing
- Function Execution Context (FEC) - created each time a function is invoked
- Eval Execution Context - created when code runs inside
eval()(rarely used in modern JavaScript)
Global Execution Context (GEC)โ
When is it created?โ
The Global Execution Context is created when your JavaScript file starts executing. There is only one GEC in your entire program, and it's stored at the bottom of the Call Stack.
What happens inside the Global EC?โ
The GEC goes through two phases:
1. Memory Creation Phase (Hoisting)โ
During this phase, JavaScript allocates memory for variables and functions:
vardeclarations โ initialized toundefinedletandconstโ placed in the Temporal Dead Zone (TDZ)- Function declarations โ fully hoisted with their definitions
thisโ points towindow(browser) orglobal(Node.js)
console.log(a); // undefined (hoisted but not yet assigned)
var a = 10;
2. Execution Phaseโ
During this phase, the code runs line by line:
- Variables are assigned their actual values
- Functions are invoked
- Expressions are evaluated
Function Execution Context (FEC)โ
When is it created?โ
A new Function Execution Context is created each time a function is invoked. When the function completes, its execution context is destroyed.
What happens inside the Function EC?โ
Like the GEC, the FEC also has two phases:
Memory Creation Phaseโ
- Function parameters โ initialized to
undefined - Local variables โ hoisted according to their declaration type
- Inner functions โ hoisted
thisโ depends on how the function is called
Execution Phaseโ
- Parameters and variables are assigned values
- Statements are executed
- Return value is computed
Call Stackโ
The Call Stack is a data structure that tracks execution contexts. It follows the Last In, First Out (LIFO) principle.
Example:โ
function one() {
two();
}
function two() {
console.log('Hello');
}
one();
Stack Flow:โ
1. Global EC (pushed)
2. one() EC (pushed)
3. two() EC (pushed)
4. two() EC (popped - after console.log)
5. one() EC (popped - after two() returns)
6. Global EC remains until program ends
Visual Representation:โ
Call Stack at peak:
| two() | โ Top
| one() |
| GEC | โ Bottom
The this Keyword in Execution Contextโ
The value of this depends on the execution context:
In Global EC:โ
console.log(this); // window (browser) / global (Node.js)
In Function EC:โ
function test() {
console.log(this);
}
test(); // window (non-strict mode), undefined (strict mode)
In Method Call:โ
const obj = {
fn: function() {
console.log(this);
}
};
obj.fn(); // this โ obj
Lexical vs Variable Environmentโ
Both are part of the execution context but handle different types of declarations:
Lexical Environmentโ
- Stores variables declared with
letandconst - Block-scoped
- Respects block boundaries (
{})
Variable Environmentโ
- Stores variables declared with
var - Function-scoped
- Ignores block boundaries (except function blocks)
Hoisting Summaryโ
| Declaration | Hoisted? | Initial Value |
|---|---|---|
var | โ Yes | undefined |
let | โ Yes | TDZ (Temporal Dead Zone) |
const | โ Yes | TDZ (Temporal Dead Zone) |
function | โ Yes | Function reference |
| Arrow function | โ No | TDZ |
Complete Execution Flow Exampleโ
Let's trace through a complete example:
var x = 1;
function foo(a) {
var y = 2;
function bar() {
console.log(a, x, y);
}
bar();
}
foo(10);
Execution Order:โ
-
Global EC created
xhoisted โundefinedfoohoisted โ function reference
-
Execution phase begins
xassigned1
-
foo(10)called โ Function EC createdaassigned10yhoisted โundefinedbarhoisted โ function referenceyassigned2
-
bar()called โ New Function EC created- Looks up
aโ finds10infoo's scope - Looks up
xโ finds1in global scope - Looks up
yโ finds2infoo's scope - Logs:
10 1 2
- Looks up
-
Contexts are popped in reverse order
Execution Context vs Scopeโ
These concepts are often confused but are fundamentally different:
| Aspect | Execution Context | Scope |
|---|---|---|
| Definition | Environment where code runs | Where variables are accessible |
| Created | At runtime | At definition time (lexical) |
| Storage | Call Stack | Lexical structure |
| Lifetime | Destroyed after execution | Can survive via closures |
One-liner:โ
Execution context is runtime, scope is lexical.
Example:โ
function outer() {
var x = 10;
function inner() {
console.log(x);
}
return inner;
}
const fn = outer();
fn(); // 10
In this example:
outer()'s execution context is destroyed โ- But
xis still accessible โ โ this is a closure - Scope survives EC destruction
Closures and Execution Contextโ
Definitionโ
A closure is created when a function remembers variables from its lexical scope, even after the outer function's execution context is gone.
How Closures Form (Internals)โ
function counter() {
let count = 0;
return function() {
count++;
console.log(count);
};
}
const inc = counter();
inc(); // 1
inc(); // 2
What Happens Internally:โ
counter()โ Function EC createdcountstored in Lexical Environment- Inner function returned
counter()EC popped from Call Stackcountretained via closure (reference maintained)
Memory Model:โ
Heap:
โโโ LexicalEnv { count: 0 }
Call Stack:
โโโ inner() EC
Important Points:โ
- Closures don't copy values โ
- Closures keep references โ
- Common use cases:
- Event handlers
setTimeout- Currying
- Memoization
Execution Context and Event Loopโ
Understanding how execution contexts interact with the event loop is crucial:
console.log('A');
setTimeout(() => console.log('B'), 0);
console.log('C');
Execution Flow:โ
- Global EC created
'A'loggedsetTimeoutโ callback sent to Web APIs'C'logged- Global EC completes
- Event loop pushes callback to Call Stack
'B'logged
Output:โ
A
C
B
Key Rule:โ
The Call Stack must be empty before the Event Loop can push callbacks from the queue.
Microtasks and Execution Contextโ
console.log('A');
Promise.resolve().then(() => console.log('B'));
console.log('C');
Output:โ
A
C
B
Why?โ
Microtasks (like Promise.then) are executed:
- After the current execution context finishes
- Before macrotasks (like
setTimeout)
Priority Order:โ
1. Call Stack
2. Microtask Queue (Promises, queueMicrotask)
3. Macrotask Queue (setTimeout, setInterval, I/O)
Common Interview Questionsโ
Q: Does closure increase memory usage?โ
A: Yes, if references are retained unnecessarily. Closures keep variables in memory even after the outer function completes.
Q: Is a closure created on function call or definition?โ
A: Definition time (lexical scope). The closure is formed when the function is defined, not when it's called.
Q: Does a block create an execution context?โ
A: No โ. Only functions create execution contexts. Blocks create lexical scope but not execution contexts.
Mental Modelsโ
| Concept | Think of it as |
|---|---|
| Execution Context | Stack frame in memory |
| Scope | Variable visibility rules |
| Closure | Preserved lexical environment |
| Event Loop | Stack coordinator |
Interview One-Linersโ
Execution Context: "An execution context is the environment where JavaScript code runs, consisting of memory creation and execution phases, created globally once and per function call thereafter."
EC vs Scope vs Closure: "Execution contexts manage runtime execution, scopes define variable access, and closures allow lexical environments to survive beyond their execution contexts."
Execution Context Lifecycleโ
Code runs
โ
Execution Context created
โ
Memory phase (hoisting)
โ
Execution phase
โ
Context popped from stack
โ
Next context runs (if any)
Key Takeawaysโ
- Execution contexts are created at runtime and manage code execution
- Scope is determined at definition time and controls variable access
- Closures allow inner functions to access outer variables even after the outer function has finished
- The Call Stack tracks all active execution contexts
- The Event Loop coordinates between the Call Stack and task queues
- Microtasks have higher priority than macrotasks