LearnContact
Lesson 440 min read

How JavaScript Works

Learn how JavaScript code is executed from start to finish, including execution contexts, the call stack, memory creation, the execution phase, synchronous execution, and the role of the runtime environment.

Introduction

Writing JavaScript code is only the beginning. When a JavaScript file is loaded, several important processes happen behind the scenes before the program produces its final output.

JavaScript must create an environment for the program, allocate memory for variables and functions, execute statements in the correct order, manage function calls, and communicate with the surrounding runtime environment.

Understanding this process will make advanced concepts such as scope, hoisting, callbacks, promises, asynchronous programming, and the event loop much easier to understand later.

What You Will Learn
  • How JavaScript code moves from a file to execution.
  • What a JavaScript runtime environment is.
  • What an execution context is.
  • The difference between global and function execution contexts.
  • How the Memory Creation Phase works.
  • How the Code Execution Phase works.
  • How the Call Stack manages function execution.
  • Why JavaScript is called synchronous.
  • Why JavaScript is called single-threaded.
  • How browser APIs help with asynchronous operations.
  • What the Callback Queue does.
  • How the Event Loop coordinates asynchronous code.

The Big Picture

JavaScript execution involves more than simply reading one line and immediately displaying output. The source code passes through several stages and works with multiple components.

JavaScript Execution Overview
JavaScript File
       ↓
Runtime Environment Loads Code
       ↓
JavaScript Engine Processes Code
       ↓
Global Execution Context Created
       ↓
Memory Creation Phase
       ↓
Code Execution Phase
       ↓
Functions Create New Execution Contexts
       ↓
Call Stack Manages Execution
       ↓
Runtime APIs Handle External Operations
       ↓
Program Produces Output

Each part of this process has a specific responsibility. Together, these parts allow JavaScript applications to execute correctly.

Real-World Analogy

Imagine a restaurant kitchen. A customer places an order, the kitchen prepares a workspace, ingredients are collected, tasks are performed in order, and completed dishes are delivered.

JavaScript Code

The instructions or recipe describing what should happen.

JavaScript Engine

The chef responsible for understanding and executing the instructions.

Execution Context

The workspace containing the information needed to perform a task.

Call Stack

The system that tracks which task is currently being performed.

Runtime Environment

The complete restaurant containing additional services and resources.

Event Loop

The coordinator that checks when completed external tasks can continue.

Restaurant Analogy
Customer Order
      ↓
Recipe Received
      ↓
Kitchen Workspace Created
      ↓
Ingredients Prepared
      ↓
Instructions Executed
      ↓
Additional Tasks Managed
      ↓
Final Dish Delivered

From JavaScript File to Execution

Consider a simple JavaScript file containing variables, a function, and a function call.

app.js
const firstNumber = 10;
const secondNumber = 20;

function add(a, b) {
    return a + b;
}

const result = add(firstNumber, secondNumber);

console.log(result);
Output

To produce this output, JavaScript must process the program, prepare memory, execute the global code, call the add() function, return the calculated value, and finally display the result.

Simplified Process
Load app.js
    ↓
Process Source Code
    ↓
Create Global Environment
    ↓
Prepare Variables and Functions
    ↓
Execute Statements
    ↓
Call add()
    ↓
Calculate 10 + 20
    ↓
Return 30
    ↓
Display 30

JavaScript Runtime Environment

A JavaScript runtime environment is the complete environment in which JavaScript code executes.

The runtime contains a JavaScript engine and additional features provided by the environment. A web browser is one example of a JavaScript runtime environment.

Simplified Browser Runtime
Browser Runtime Environment
│
├── JavaScript Engine
│   ├── Call Stack
│   ├── Memory Heap
│   └── Garbage Collector
│
├── Browser APIs
│   ├── DOM
│   ├── Timers
│   ├── Events
│   ├── Network Requests
│   └── Storage
│
├── Callback Queue
│
└── Event Loop
Important Difference
  • The JavaScript engine executes the JavaScript language.
  • The runtime environment contains the engine and additional features.
  • The browser provides APIs that are not part of the core JavaScript language.
  • Different environments can provide different APIs.

Main Runtime Components

To understand how JavaScript works, you should know the main components involved in execution.

JavaScript Engine

Processes and executes JavaScript code.

Call Stack

Tracks currently executing code and function calls.

Memory Heap

Stores dynamically allocated application data.

Runtime APIs

Provide additional capabilities such as timers, DOM operations, and network requests.

Callback Queue

Stores callbacks that are ready to return to JavaScript execution.

Event Loop

Coordinates the movement of ready tasks back to the Call Stack.

What is an Execution Context?

An execution context is an internal environment created by JavaScript to manage and execute code.

You can think of an execution context as a workspace containing the information JavaScript needs while running a particular section of code.

Execution Context
Execution Context
│
├── Memory for Variables
├── Function Declarations
├── Scope Information
├── Current Execution State
└── Code to Execute
Simple Definition
  • JavaScript code needs an environment in which to run.
  • That environment is called an execution context.
  • The main program receives a global execution context.
  • Every function call creates a new function execution context.

Types of Execution Context

JavaScript mainly creates execution contexts for the global program and for function calls.

Global Execution Context

Created when the JavaScript program first begins executing.

Function Execution Context

Created every time a function is called.

Execution Context Creation
JavaScript Program Starts
          ↓
Global Execution Context Created
          ↓
Function is Called
          ↓
Function Execution Context Created
          ↓
Function Completes
          ↓
Function Context Removed

Global Execution Context

When JavaScript begins executing a script, it creates the Global Execution Context. This is the first execution context created for the program.

Global Code
const course = 'JavaScript';
const lessons = 27;

console.log(course);
console.log(lessons);
Output

The declarations and statements at the top level of the script are processed as part of the global execution context.

Global Execution
Script Starts
     ↓
Global Execution Context
     ↓
Create course
     ↓
Create lessons
     ↓
Display course
     ↓
Display lessons
     ↓
Script Completes
Global Context Facts
  • It is created when the script begins.
  • Only one global execution context exists for a particular script execution.
  • Global code executes inside it.
  • Function calls can create additional execution contexts.

Function Execution Context

Every time a function is called, JavaScript creates a new Function Execution Context for that specific function call.

Function Call
function greet(name) {
    const message = 'Hello, ' + name;
    return message;
}

const result = greet('Rahul');

console.log(result);
Output
Execution Context Flow
Global Execution Context
│
├── Create greet()
├── Create result
│
└── Call greet('Rahul')
          ↓
Function Execution Context
│
├── name = 'Rahul'
├── Create message
├── Build 'Hello, Rahul'
└── Return Result
          ↓
Function Context Removed
          ↓
Global Execution Continues
Every Call is Separate
  • Calling a function creates a new execution context.
  • Calling the same function again creates another new context.
  • Each call can receive different arguments.
  • Each function execution has its own local data.

Two Phases of an Execution Context

A simplified model of JavaScript execution divides each execution context into two important phases.

1️⃣ Memory Creation Phase

JavaScript prepares memory for declarations before executing statements.

2️⃣ Code Execution Phase

JavaScript executes statements and updates values.

Two-Phase Model
Execution Context Created
          ↓
Phase 1
Memory Creation
          ↓
Phase 2
Code Execution
          ↓
Execution Context Completes

1. Memory Creation Phase

Before executing the program statements, JavaScript processes declarations and prepares the environment required for execution.

Example Program
var firstNumber = 10;

function add(a, b) {
    return a + b;
}

var result = add(firstNumber, 20);

In a simplified explanation, JavaScript first prepares memory for the declared variables and function before executing the assignment statements.

Simplified Memory Creation
Memory Creation Phase

firstNumber → undefined

add → Complete Function Definition

result → undefined
Important About let and const
  • var declarations are commonly explained as receiving undefined during creation.
  • let and const declarations are also created before execution.
  • However, let and const cannot be accessed before their declaration is initialized.
  • This behavior will be explained fully in the Hoisting lesson.

2. Code Execution Phase

After memory has been prepared, JavaScript begins executing statements in order.

Program
var firstNumber = 10;

function add(a, b) {
    return a + b;
}

var result = add(firstNumber, 20);

console.log(result);
Code Execution Phase
firstNumber = 10
        ↓
add() Already Available
        ↓
Call add(10, 20)
        ↓
Create Function Execution Context
        ↓
Calculate 10 + 20
        ↓
Return 30
        ↓
result = 30
        ↓
Display 30
Output

Example 1: How Variables Execute

Variables Example
var name = 'JavaScript';
var year = 1995;

console.log(name);
console.log(year);
Output
Phase 1: Memory Creation
name → undefined

year → undefined
Phase 2: Code Execution
name = 'JavaScript'
        ↓
year = 1995
        ↓
console.log(name)
        ↓
JavaScript
        ↓
console.log(year)
        ↓
1995

Example 2: How a Function Executes

Function Example
function multiply(a, b) {
    const result = a * b;
    return result;
}

const answer = multiply(5, 4);

console.log(answer);
Output
Complete Function Flow
Global Context Starts
        ↓
multiply() Function Available
        ↓
Call multiply(5, 4)
        ↓
Create Function Context
        ↓
a = 5
b = 4
        ↓
result = 5 × 4
        ↓
result = 20
        ↓
Return 20
        ↓
Function Context Removed
        ↓
answer = 20
        ↓
Display 20

The Call Stack

The Call Stack is a data structure used by JavaScript to manage execution contexts.

The execution context currently at the top of the Call Stack is the one JavaScript is actively executing.

Basic Call Stack
Program Starts

┌────────────────────────────┐
│ Global Execution Context   │
└────────────────────────────┘

Function Called

┌────────────────────────────┐
│ Function Execution Context │ ← Running
├────────────────────────────┤
│ Global Execution Context   │
└────────────────────────────┘

Function Completes

┌────────────────────────────┐
│ Global Execution Context   │ ← Continues
└────────────────────────────┘

Call Stack Step by Step

Call Stack Example
function greet() {
    console.log('Hello');
}

greet();

console.log('Finished');
Output
Step 1: Global Context
┌─────────────────────┐
│ Global Context      │
└─────────────────────┘
Step 2: greet() Called
┌─────────────────────┐
│ greet() Context     │ ← Executing
├─────────────────────┤
│ Global Context      │
└─────────────────────┘
Step 3: greet() Completes
┌─────────────────────┐
│ Global Context      │ ← Continues
└─────────────────────┘
Step 4: Program Completes
Call Stack

Empty

LIFO Principle

The Call Stack follows the LIFO principle: Last In, First Out.

The most recently added function execution context must complete before the previous context can continue.

LIFO
Last In
   ↓
┌─────────┐
│ third() │ ← Removed First
├─────────┤
│second() │
├─────────┤
│ first() │
├─────────┤
│ Global  │
└─────────┘
   ↑
First In
Think of a Stack of Plates
  • A new plate is placed on top.
  • The top plate must be removed first.
  • Function contexts work in a similar way.
  • The most recently called function finishes first.

Nested Function Calls

Nested Calls
function first() {
    console.log('First Start');
    second();
    console.log('First End');
}

function second() {
    console.log('Second Start');
    third();
    console.log('Second End');
}

function third() {
    console.log('Third');
}

first();
Output
Call Stack Growth
1. Global

2. Global → first()

3. Global → first() → second()

4. Global → first() → second() → third()

5. third() completes

6. second() continues and completes

7. first() continues and completes

8. Global continues

The output order follows the Call Stack. When first() calls second(), first() pauses. When second() calls third(), second() pauses. After third() completes, execution returns to second(), and then back to first().

JavaScript is Synchronous

Synchronous execution means that JavaScript normally executes one statement after another in order.

Synchronous Execution
console.log('Step 1');
console.log('Step 2');
console.log('Step 3');
Output
Execution Order
Step 1
   ↓
Complete
   ↓
Step 2
   ↓
Complete
   ↓
Step 3
   ↓
Complete
Synchronous Means
  • Statements execute in sequence.
  • The current operation completes before the next synchronous operation begins.
  • Long-running synchronous code can delay everything that follows it.

JavaScript is Single-Threaded

JavaScript is commonly described as single-threaded because one main Call Stack executes JavaScript code.

Single Main Execution Thread
One Call Stack

Task A
  ↓
Task B
  ↓
Task C
  ↓
Task D

Only one JavaScript execution context can occupy the active top position of the main Call Stack at a time.

Do Not Confuse These Ideas
  • Single-threaded describes the main JavaScript execution model.
  • The surrounding runtime environment can perform other work.
  • Browsers can handle timers, networking, rendering, and other operations outside the main JavaScript Call Stack.
  • This cooperation makes asynchronous programming possible.

Blocking Code

Because synchronous JavaScript uses one main Call Stack, a long-running operation can block later code from executing.

Blocking Example
console.log('Start');

for (let i = 0; i < 1000000000; i++) {
    // Long-running work
}

console.log('End');
Output

The final statement cannot execute until the loop finishes. In a browser, extremely long synchronous work can also make the page feel frozen or unresponsive.

Blocking Flow
Start
  ↓
Long Operation Begins
  ↓
Call Stack Remains Busy
  ↓
Other JavaScript Waits
  ↓
Long Operation Completes
  ↓
End

Asynchronous Operations

JavaScript applications frequently need to wait for operations such as timers, user interactions, and network requests. Blocking the Call Stack during every wait would make applications inefficient.

Runtime environments provide additional systems that can handle certain operations outside the main JavaScript Call Stack.

Timers

A timer can wait outside the main Call Stack.

User Events

The browser can listen for clicks and other interactions.

Network Requests

Network operations can progress without continuously occupying the JavaScript Call Stack.

Browser Operations

The browser environment provides many capabilities beyond the core language.

Browser APIs

Browser APIs are features provided by the browser environment. They allow JavaScript to interact with webpages and request services that are not part of the core JavaScript language.

Examples of Browser Features
// Timer API
setTimeout(() => {
    console.log('Timer Finished');
}, 1000);

// DOM API
document.querySelector('button');

// Browser Storage API
localStorage.setItem('theme', 'dark');
Core JavaScript vs Browser APIs
  • Variables are part of JavaScript.
  • Functions are part of JavaScript.
  • Arrays and objects are part of JavaScript.
  • The DOM is provided by the browser.
  • Browser timers are provided by the runtime environment.
  • Browser storage is provided by the browser.

Callback Queue

When certain asynchronous operations complete, their callbacks may become ready to execute. Ready callbacks can wait in a queue until JavaScript is able to process them.

Simplified Queue Flow
Asynchronous Operation Starts
          ↓
Runtime Handles Operation
          ↓
Operation Completes
          ↓
Callback Becomes Ready
          ↓
Callback Queue
          ↓
Wait for Call Stack
Why a Queue is Needed
  • The Call Stack may already be busy.
  • Ready callbacks cannot interrupt currently executing synchronous JavaScript.
  • They wait until JavaScript can process them.

Event Loop

The Event Loop coordinates asynchronous execution by repeatedly checking whether the Call Stack is available and whether queued work is ready to execute.

Event Loop Model
        ┌─────────────────┐
        │   Call Stack    │
        └────────┬────────┘
                 │
                 │
        Is Stack Available?
                 │
                 ↓
        ┌─────────────────┐
        │   Event Loop    │
        └────────┬────────┘
                 │
                 ↓
        ┌─────────────────┐
        │ Callback Queue  │
        └─────────────────┘

When the Call Stack becomes available, the Event Loop allows ready queued work to move toward execution.

Important Simplification
  • The complete asynchronous model contains additional queue types and scheduling rules.
  • Promises use a different queue mechanism from traditional timer callbacks.
  • Those advanced details will be easier to understand in later JavaScript lessons.
  • For now, understand the relationship between the Call Stack, runtime APIs, queues, and the Event Loop.

Asynchronous Execution Example

Timer Example
console.log('Start');

setTimeout(() => {
    console.log('Timer');
}, 0);

console.log('End');
Output

Even though the timer delay is zero, the callback does not interrupt the currently executing synchronous code.

Step-by-Step Execution
1. console.log('Start')
          ↓
Output: Start
          ↓
2. setTimeout() Called
          ↓
Timer Handled by Runtime
          ↓
3. console.log('End')
          ↓
Output: End
          ↓
Global Synchronous Code Completes
          ↓
Timer Callback is Ready
          ↓
Event Loop Checks Call Stack
          ↓
Callback Executes
          ↓
Output: Timer
Why Timer Does Not Appear Second
  • The timer callback is asynchronous.
  • The current synchronous code continues first.
  • The callback waits until it is allowed to execute.
  • A delay of zero does not mean immediate interruption of the Call Stack.

Complete JavaScript Execution Flow

Complete Example
console.log('Program Started');

function calculateTotal(price, quantity) {
    return price * quantity;
}

const total = calculateTotal(500, 3);

setTimeout(() => {
    console.log('Timer Completed');
}, 0);

console.log('Total:', total);

console.log('Program Finished');
Output
Complete Flow
1. Script Loaded
        ↓
2. Global Execution Context Created
        ↓
3. Memory Prepared
        ↓
4. Program Started Displayed
        ↓
5. calculateTotal() Called
        ↓
6. Function Context Created
        ↓
7. 500 × 3 Calculated
        ↓
8. Function Returns 1500
        ↓
9. Function Context Removed
        ↓
10. Timer Registered with Runtime
        ↓
11. Total: 1500 Displayed
        ↓
12. Program Finished Displayed
        ↓
13. Synchronous Code Completes
        ↓
14. Timer Callback Becomes Eligible
        ↓
15. Event Loop Coordinates Execution
        ↓
16. Timer Completed Displayed

JavaScript Engine vs Runtime Environment

The JavaScript engine and the runtime environment work together, but they have different responsibilities.

JavaScript Engine

Processes JavaScript syntax, creates execution contexts, manages the Call Stack, executes code, and manages memory.

Runtime Environment

Contains the engine and provides additional APIs and systems required by applications.

Engine and Runtime
JavaScript Runtime
│
├── JavaScript Engine
│   ├── Execute JavaScript
│   ├── Call Stack
│   ├── Memory Management
│   └── Garbage Collection
│
└── Runtime Features
    ├── Timers
    ├── DOM
    ├── Events
    ├── Networking
    ├── Storage
    └── Async Coordination

Stack Overflow

The Call Stack has limited capacity. If functions continue calling themselves without reaching a stopping condition, the stack can grow until the environment cannot add more execution contexts.

Infinite Recursion
function repeat() {
    repeat();
}

repeat();
Result
Stack Growth
Global
  ↓
repeat()
  ↓
repeat()
  ↓
repeat()
  ↓
repeat()
  ↓
repeat()
  ↓
...
  ↓
Maximum Stack Capacity Reached
  ↓
Stack Overflow Error
Prevent Infinite Recursion
  • Recursive functions need a stopping condition.
  • Every recursive call adds another function context.
  • Without a base condition, the Call Stack continues growing.

Common Beginner Mistakes

Avoid These Mistakes
  • Thinking JavaScript executes code without creating an execution environment.
  • Thinking the JavaScript engine and runtime environment are the same thing.
  • Assuming every function shares one execution context.
  • Forgetting that every function call creates a new function execution context.
  • Confusing the Memory Heap with the Call Stack.
  • Thinking asynchronous callbacks interrupt synchronous JavaScript immediately.
  • Assuming setTimeout with zero delay executes instantly.
  • Thinking browser APIs are part of the core JavaScript language.
  • Forgetting that long-running synchronous code can block later execution.
  • Using recursion without a proper stopping condition.

Best Practices

  • Understand execution contexts before studying scope and hoisting.
  • Visualize function calls using the Call Stack.
  • Keep synchronous tasks reasonably short in browser applications.
  • Avoid unnecessary long-running loops on the main execution thread.
  • Always provide a valid stopping condition for recursive functions.
  • Separate core JavaScript features from runtime-provided APIs.
  • Use console output to study the order in which code executes.
  • Do not assume asynchronous callbacks execute immediately.
  • Learn the basic Event Loop model before moving to promises and async/await.
  • Focus on the overall execution flow before studying engine-specific implementation details.

Frequently Asked Questions

What happens when JavaScript code starts running?

The runtime loads the code, the engine processes it, a global execution context is created, memory is prepared, and statements begin executing.

What is an execution context?

An execution context is the internal environment JavaScript creates to manage and execute code.

What is the Global Execution Context?

It is the first execution context created when a JavaScript script begins running.

When is a Function Execution Context created?

A new Function Execution Context is created every time a function is called.

What happens during the Memory Creation Phase?

JavaScript prepares the environment for declarations before executing the program statements.

What happens during the Code Execution Phase?

JavaScript executes statements, performs calculations, assigns values, calls functions, and produces results.

What is the Call Stack?

The Call Stack is a structure that tracks active execution contexts and determines which code is currently running.

What does LIFO mean?

LIFO means Last In, First Out. The most recently added function context is removed before earlier contexts continue.

Why is JavaScript called synchronous?

JavaScript normally executes synchronous statements one after another in order.

Why is JavaScript called single-threaded?

The main JavaScript execution model uses one primary Call Stack to execute JavaScript code.

How can JavaScript perform asynchronous tasks?

The surrounding runtime environment handles certain operations outside the main Call Stack and coordinates their callbacks with queues and the Event Loop.

What is the Event Loop?

The Event Loop coordinates when ready asynchronous work can continue after the Call Stack becomes available.

Why does setTimeout with zero delay not execute immediately?

The callback must wait until the current synchronous code finishes and the runtime scheduling process allows it to execute.

What causes a stack overflow?

A stack overflow occurs when too many execution contexts are added to the Call Stack, commonly because of uncontrolled recursion.

Key Takeaways

  • JavaScript code executes inside a runtime environment.
  • The JavaScript engine is responsible for processing and executing the language.
  • The runtime environment provides additional features and APIs.
  • JavaScript creates a Global Execution Context when a script begins.
  • Every function call creates a new Function Execution Context.
  • Execution contexts can be understood using a Memory Creation Phase and a Code Execution Phase.
  • The Call Stack manages active execution contexts.
  • The Call Stack follows the Last In, First Out principle.
  • Nested function calls cause the Call Stack to grow.
  • JavaScript normally executes synchronous statements sequentially.
  • The main JavaScript execution model uses one primary Call Stack.
  • Long-running synchronous code can block later execution.
  • Runtime environments can handle certain operations outside the main Call Stack.
  • Browser APIs provide features such as timers, DOM access, events, networking, and storage.
  • Ready asynchronous callbacks wait until JavaScript can execute them.
  • The Event Loop coordinates asynchronous work with the Call Stack.
  • A zero-delay timer does not interrupt currently executing synchronous code.
  • Infinite recursion can cause a maximum Call Stack error.

Summary

JavaScript execution begins when the runtime environment loads a script and the JavaScript engine processes the code. The engine creates a Global Execution Context, prepares the required memory, and then executes the program statements.

Every function call creates a separate Function Execution Context. These contexts are managed using the Call Stack, which follows the Last In, First Out principle.

JavaScript executes synchronous code using one main Call Stack. However, the surrounding runtime environment provides additional capabilities for timers, events, networking, DOM operations, and other tasks. Queues and the Event Loop help coordinate asynchronous work with the main JavaScript execution process.

Understanding execution contexts, the Call Stack, synchronous execution, runtime APIs, and the Event Loop creates the foundation for later topics such as variables, scope, hoisting, functions, events, promises, and asynchronous JavaScript.

Lesson 4 Completed
  • You understand the complete JavaScript execution overview.
  • You know what a runtime environment is.
  • You understand execution contexts.
  • You know the difference between global and function execution contexts.
  • You understand the Memory Creation and Code Execution phases.
  • You know how the Call Stack manages function calls.
  • You understand the LIFO principle.
  • You know why JavaScript is called synchronous and single-threaded.
  • You understand how blocking code affects execution.
  • You know the roles of Browser APIs, the Callback Queue, and the Event Loop.
  • You are ready to learn JavaScript Variables.
Next Lesson →

JavaScript Variables