From Zero to AI

Lesson 1.1: How JavaScript Executes Code

Duration: 50 minutes

Learning Objectives

By the end of this lesson, you will be able to:

  • Understand what a JavaScript engine is and how it works
  • Explain why JavaScript is single-threaded
  • Describe the JavaScript runtime environment
  • Understand the difference between the engine and the runtime

The JavaScript Engine

When you write JavaScript code, the computer cannot understand it directly. It needs a program to translate and execute your code. This program is called a JavaScript engine.

// Your code
const message = 'Hello, World!';
console.log(message);

// The engine reads this, understands it, and executes it

Different browsers and environments use different engines:

Engine Used In Creator
V8 Chrome, Node.js, Deno Google
SpiderMonkey Firefox Mozilla
JavaScriptCore Safari Apple
Chakra Edge (legacy) Microsoft

When you run TypeScript code, it first compiles to JavaScript, then the engine executes that JavaScript.

// TypeScript
const greet = (name: string): string => {
  return `Hello, ${name}!`;
};

// Compiles to JavaScript, then V8 (or another engine) executes it

Single-Threaded Execution

JavaScript is single-threaded, meaning it can only do one thing at a time. Think of it like a single cashier at a store - they can only serve one customer at a time.

console.log('First'); // Executes first
console.log('Second'); // Waits, then executes
console.log('Third'); // Waits, then executes

// Output:
// First
// Second
// Third

The code executes from top to bottom, one line at a time. The engine finishes one operation before starting the next.

Why Single-Threaded?

JavaScript was created for the browser to manipulate web pages. Having multiple threads changing the same page elements could cause chaos:

// Imagine two threads running at the same time:
// Thread 1: Delete the button
// Thread 2: Change the button's color

// Which happens first? This would be unpredictable!

Single-threaded execution makes code predictable - you always know what runs when.

The Problem with Single-Threaded

If JavaScript can only do one thing at a time, what happens with slow operations?

// Imagine this takes 5 seconds
const data = fetchDataFromServer(); // Blocks everything!

// This has to wait 5 seconds to run
console.log('Ready!');

If fetching data takes 5 seconds, the entire program freezes. The user cannot click buttons, scroll, or do anything. This is called blocking.

We will solve this problem later in this module - that is where the event loop comes in!


The Runtime Environment

The JavaScript engine alone cannot do much. It needs a runtime environment that provides additional capabilities.

What the Engine Provides

The engine handles core JavaScript:

  • Variables and data types
  • Functions and scope
  • Objects and arrays
  • Loops and conditions

What the Runtime Provides

The runtime adds extra features:

In the Browser:

  • DOM manipulation (document.querySelector)
  • HTTP requests (fetch)
  • Timers (setTimeout, setInterval)
  • Storage (localStorage)
  • User events (clicks, keyboard input)

In Node.js:

  • File system access (fs)
  • Network operations (http)
  • Timers (setTimeout, setInterval)
  • Process management (process)
  • Operating system info (os)
// This is JavaScript (engine handles it)
const numbers = [1, 2, 3];
const doubled = numbers.map((n) => n * 2);

// This is the runtime (browser provides it)
document.getElementById('app');
setTimeout(() => console.log('Timer!'), 1000);
fetch('https://api.example.com/data');

The Complete Picture

┌─────────────────────────────────────────────────────────┐
│                    Runtime Environment                   │
│  ┌─────────────────┐    ┌─────────────────────────────┐ │
│  │   JavaScript    │    │        Web APIs /           │ │
│  │     Engine      │    │        Node APIs            │ │
│  │                 │    │                             │ │
│  │  - Variables    │    │  - setTimeout/setInterval   │ │
│  │  - Functions    │    │  - fetch / http             │ │
│  │  - Objects      │    │  - DOM (browser)            │ │
│  │  - Arrays       │    │  - File system (Node)       │ │
│  │  - Loops        │    │  - Events                   │ │
│  └─────────────────┘    └─────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘

How Code Execution Works

Let us trace through a simple program:

const name = 'Alice';

function greet(person: string): void {
  const greeting = `Hello, ${person}!`;
  console.log(greeting);
}

greet(name);
console.log('Done');

Step-by-Step Execution

  1. Line 1: Engine creates variable name with value "Alice"
  2. Lines 3-6: Engine stores the function greet in memory (does not run it yet)
  3. Line 8: Engine sees greet(name):
    • Calls the function with "Alice"
    • Creates greeting variable inside the function
    • Executes console.log(greeting) - prints "Hello, Alice!"
    • Function ends, returns to line 8
  4. Line 9: Engine executes console.log("Done") - prints "Done"
Output:
Hello, Alice!
Done

Every line completes before the next one starts. This is synchronous execution.


Execution Context

When JavaScript runs code, it creates an execution context - a wrapper that keeps track of what is happening.

Global Execution Context

When your program starts, JavaScript creates a global context:

// Global context is created
const appName = 'MyApp';

function initialize(): void {
  console.log('Starting ' + appName);
}

initialize();

The global context contains:

  • Global variables (appName)
  • Function declarations (initialize)
  • The this keyword (pointing to global object)

Function Execution Context

When a function is called, a new context is created:

function calculateArea(width: number, height: number): number {
  // New execution context created here
  const area = width * height;
  return area;
  // Context destroyed when function ends
}

const result = calculateArea(5, 10);

Each function call gets its own context with:

  • Parameters (width, height)
  • Local variables (area)
  • Its own this value

Memory Management

The engine manages memory in two areas:

The Heap

Stores objects and complex data:

// These are stored in the heap
const user = { name: 'Alice', age: 25 };
const numbers = [1, 2, 3, 4, 5];

The Stack

Stores simple values and tracks function calls:

// These might be stored on the stack
const count = 42;
const isActive = true;

We will explore the stack in detail in the next lesson when we cover the call stack.


Practical Example

Let us see how execution order works in practice:

function multiply(a: number, b: number): number {
  console.log(`Multiplying ${a} * ${b}`);
  return a * b;
}

function square(n: number): number {
  console.log(`Squaring ${n}`);
  return multiply(n, n);
}

function printSquare(n: number): void {
  console.log(`Starting with ${n}`);
  const result = square(n);
  console.log(`Result: ${result}`);
}

printSquare(5);

Execution Trace

1. printSquare(5) is called
   -> "Starting with 5"

2. square(5) is called
   -> "Squaring 5"

3. multiply(5, 5) is called
   -> "Multiplying 5 * 5"
   -> returns 25

4. square returns 25

5. printSquare continues
   -> "Result: 25"

Output:

Starting with 5
Squaring 5
Multiplying 5 * 5
Result: 25

Exercises

Exercise 1: Predict the Output

What will this code print?

console.log('A');

function first(): void {
  console.log('B');
}

function second(): void {
  console.log('C');
  first();
  console.log('D');
}

console.log('E');
second();
console.log('F');
Solution
A
E
C
B
D
F

Explanation:

  1. console.log("A") - prints "A"
  2. Functions first and second are defined (not called yet)
  3. console.log("E") - prints "E"
  4. second() is called:
    • prints "C"
    • calls first() which prints "B"
    • prints "D"
  5. console.log("F") - prints "F"

Exercise 2: Engine vs Runtime

Categorize each feature as "Engine" or "Runtime":

  1. const x = 5
  2. setTimeout(fn, 1000)
  3. array.map(fn)
  4. fetch(url)
  5. for (let i = 0; i < 10; i++)
  6. document.querySelector(".btn")
  7. function add(a, b) { return a + b; }
Solution
  1. const x = 5 - Engine (variable declaration)
  2. setTimeout(fn, 1000) - Runtime (Web API / Node API)
  3. array.map(fn) - Engine (built-in array method)
  4. fetch(url) - Runtime (Web API)
  5. for (let i = 0; i < 10; i++) - Engine (loop construct)
  6. document.querySelector(".btn") - Runtime (DOM API, browser only)
  7. function add(a, b) { return a + b; } - Engine (function declaration)

Exercise 3: Trace the Execution

Write down the order of console.log outputs:

function a(): void {
  console.log('a start');
  b();
  console.log('a end');
}

function b(): void {
  console.log('b start');
  c();
  console.log('b end');
}

function c(): void {
  console.log('c');
}

console.log('program start');
a();
console.log('program end');
Solution
program start
a start
b start
c
b end
a end
program end

The functions call each other in a chain, and each must complete before returning to the caller.


Key Takeaways

  1. JavaScript engine translates and executes your code (V8, SpiderMonkey, etc.)
  2. Single-threaded means one operation at a time - predictable but can block
  3. Runtime environment provides additional APIs (timers, HTTP, DOM, file system)
  4. Execution contexts track what is happening during code execution
  5. Synchronous execution means each line completes before the next starts

Resources

Resource Type Description
MDN: JavaScript Engine Documentation What is a JS engine
Inside the JavaScript Runtime Article Deep dive into V8
JavaScript.info: JavaScript Fundamentals Tutorial Execution basics

Next Lesson

Now that you understand how JavaScript executes code, let us explore the call stack - the data structure that tracks function execution - and introduce the event loop.

Continue to Lesson 1.2: Call Stack and Event Loop