
The Execution Context in JavaScript

Understanding how JavaScript executes code is key to writing efficient, reliable applications. In JavaScript, the execution context determines how functions run, how variables are accessed, and how the call stack operates.
In this article, I'll break down what an execution context is, how it works within the JavaScript engine, and why it matters. We'll also explore how execution contexts relate to modern development in React, Next.js, and Node.js.
What is an Execution Context?
Every time JavaScript runs a script or calls a function, it creates an execution context. This context defines how the code runs and how variables, functions, and objects are handled.
Think of it as a workspace where JavaScript keeps track of what it's currently doing. Execution contexts follow a structured lifecycle and work with the JavaScript execution stack, which manages function calls and variable scopes. Understanding this can be very helpful, especially when it comes to debugging tricky issues like unexpected variable values or function execution order, and doubly so when it comes to doing so in complex applications.
The Types of Execution Contexts
There are three main types of execution contexts in JavaScript:
1. Global Execution Context
This is the default execution context created when JavaScript first runs a script. It represents the outermost environment where global variables and functions exist.
We can illustrate this by consoling out this:
console.log(this);When this is executed on the browser, the global execution context is associated with the window object, whilst in Node.js, it is tied to global.
2. Function Execution Context
Every time a function is called, JavaScript creates a new execution context specifically for that function. This execution context contains:
- The function's arguments and local variables.
- A reference to its outer (parent) execution context.
For example:
function greet(name: string) { console.log(`Hello, ${name}`);}greet("Maddie");This example illustrates how JavaScript creates a new execution context every time a function is called. When greet("Maddie") is executed:
- A new function execution context is created and pushed onto the call stack.
- Inside the function,
nameis assigned the value "Maddie", andconsole.logruns. - Once execution completes, the function execution context is removed from the call stack, returning control to the global execution context.
In this way, I'm (hopefully) demonstrating how JavaScript isolates functional execution, allowing each function call to maintain its own local variables and execution flow. This can be particularly important in cases like recursion, where multiple execution contexts can build up, requiring careful stack management to avoid exceeding memory limits or memory leaks.
3. Eval Execution Context
eval() has solidly fallen out of favour over the past few years (with good reason), but it is still the third context and, therefore, should be discussed. Just enter this section knowing that there is virtually no reason you would ever use eval() in a production environment.
eval() is a JavaScript function which executes a string of code as if it were part of the script. It dynamically interprets and runs the provided string, evaluating expressions, executing functions, and defining variables at runtime.
In terms of execution context, each eval() function creates a separate execution context of its own. For example:
eval("console.log('Executing inside eval')");However, in modern development, eval() is generally avoided due to its potential risks where it can be exploited for injection attacks, allowing arbitrary code execution. It also raises performance concerns simply because eval() bypasses JavaScript's normal compilation and execution optimisations, which means that the JavaScript engine has to recompile the executed code every time it runs.
The Execution Context Lifecycle
Each execution context follows a three‑phase lifecycle:
1. Creation Phase
When JavaScript encounters a new execution context (global or function), it performs the following steps:
Creates the Variable Environment:
Allocates memory for variables and functions.Establishes the Scope Chain:
Sets up references to outer execution contexts.Determines
thisBinding: Decides whatthisshould refer to.
2. Execution Phase
- JavaScript executes the code inside the execution context.
- Variables get assigned values, and functions run.
- The context remains active until execution is complete.
3. Destruction Phase
- Once execution finishes, the execution context is removed from the call stack.
This process ensures JavaScript executes synchronously unless asynchronous operations (like setTimeout or Promises) are involved.
The call Stack and Execution Contexts
JavaScript is single‑threaded, which means that it executes one task at a time. Execution contexts are managed using the call stack, a data structure that follows Last In, First Out (LIFO). For example:
function first() { console.log("First function");}function second() { first(); console.log("Second function");}second();call Stack Order:
When this is called:
- The
global execution contextis created. second()is called → A new execution context is pushed onto the stack.first()is called insidesecond()→ Another execution context is pushed.first()finishes → Its execution context is popped off.second()finishes → Its execution context is popped off.
In this way, this process ensures orderly execution of JavaScript functions, and the output looks like this:
First functionSecond functionExecution Contexts in React and Next.js
Function Components and Execution Context
In React, function components follow exactly the same execution context rules as regular JavaScript functions.
function Greeting({ name }: { name: string }) { console.log("Rendering Greeting component"); return <h1>Hello, {name}!</h1>;}Being a function, this follows the Function Execution Context, with every new render creating a new execution context for the component.
Hooks and Execution
Context React Hooks (like useState and useEffect) can only be called at the top level of a function component because they rely on execution order. For example:
function Counter() { const [count, setCount] = useState(0); function increment() { setCount(count + 1); } return <button onClick={increment}>Count: {count}</button>;}If hooks were called conditionally, the execution order could become inconsistent, leading to issues like lost state or unintended re‑renders in React applications. This is a very common problem when debugging poorly performing applications or components that don't behave as expected.
Next.js API Routes and Execution Context
In Next.js, API routes run on the server and create an execution context per request.
export default function handler(req, res) { console.log("New API request"); res.status(200).json({ message: "Hello from Next.js API" });}At the end of the day, they are only functions, so it should come as no surprise that they follow the Function Execution Context, where each instantiation creates a new execution context. Of course, this also means that state doesn't persist between requests—another assumption I often see causing problems for developers!
Execution Contexts in Node.js
Node.js operates differently from the browser because it uses event‑driven architecture.
Asynchronous Execution
In contexts Unlike synchronous JavaScript, Node.js relies on event loops and callback queues:
console.log("Start");setTimeout(() => console.log("Timeout Callback"), 0);console.log("End");Even though setTimeout is called first, it executes after synchronous code because of the event loop.
Execution Context in Modules
Each module in Node.js runs in its own execution context, which prevents global variable leakage. This means that variables and functions defined in one module do not affect others, enforcing modularity and avoiding accidental overwrites.
Take ‑ for example ‑ if we had two modules like this:
// module1.tsexport const message = "Hello from Module 1";// main.tsimport { message } from "./module1";console.log(message);When this is run:
Separate Execution Contexts
:- Each module (
module1.tsandmain.ts) runs in its own execution context. - Because of this, that variables declared in one module don't leak into others.
- Each module (
Exporting a Variable
:In module1.ts, the variablemessageis declared and exported.
Importing into Another Module
:- In
main.ts,messageis imported frommodule1.ts.
- In
Logging the Output
:- When
console.log(message); runs inmain.ts, it outputsHello from Module 1.
- When
Wrapping up
The execution context is fundamental to how JavaScript works, affecting everything from variable scope to asynchronous behaviour. Whether you're writing vanilla JavaScript, working with React components, or handling API requests in Next.js, understanding execution contexts will help you write clearer and more efficient code.
If that still feels a bit abstract, it helps to remember that these mechanics do not disappear inside frameworks. On the Nando’s UK & Ireland Replatform, the same underlying JavaScript rules still shaped how code behaved across the browser, Node.js, and a fairly involved Next.js application.
Key Takeaways
- JavaScript creates an execution context for every script and function call.
- Execution contexts are managed via the call stack.
- The global execution context is created first, followed by function execution contexts.
- React function components create a new execution context per render.
- Next.js API routes and Node.js modules create isolated execution contexts.
- Understanding execution contexts helps with debugging and optimizing JavaScript performance.
Once you understand execution contexts, debugging gets easier, and your code runs more efficiently across different environments.
Related Articles

Optimising Performance in React with useMemo and useCallback. Optimising Performance in React with

Rest and Spread Operators in JavaScript: A Beginner's Guide. Rest and Spread Operators in JavaScript: A Beginner's Guide

The Differences Between Lead and Senior Roles in Front‑End Development. The Differences Between Lead and Senior Roles in Front‑End Development

Exploring CSS Viewport Units Beyond vw and vh. Exploring CSS Viewport Units Beyond
vwandvh
LocalStorage in JavaScript. localStoragein JavaScript
Sort the Keys of an Object with JavaScript. Sort the Keys of an Object with JavaScript

Flexbox vs. grid. Flexbox vs.
grid
Understanding getStaticPaths in Next.js. Understanding
getStaticPathsin Next.js
Sorting Objects in JavaScript. Sorting Objects in JavaScript

Top Reasons to Work with a Local Web Developer in Brighton. Top Reasons to Work with a Local Web Developer in Brighton

Building Custom Hooks in React. Building Custom Hooks in React

All About Headless CMSes. All About Headless CMSes