How JavaScript Runs
Understand, at a conceptual level, the journey your JavaScript source code takes from a text file to running behavior — parsing, execution contexts, the call stack, memory, and browser APIs.
Before writing more JavaScript, it helps to understand what actually happens when a browser or Node.js runs your code — the steps between "source code" and "something happening on screen."
From source code to execution
Source code is simply the plain text file you write — a .js file full of characters that are meaningless to a computer's processor on their own. Before anything can run, the JavaScript engine must first read and make sense of that text.
The engine parses the source code: it scans through the text, breaks it into meaningful tokens (keywords, variable names, operators, punctuation), and checks that they form valid JavaScript according to the language's grammar rules. If the code is not valid syntax, parsing fails immediately with a SyntaxError, and nothing runs at all — not even the correct parts.
Compilation vs interpretation
JavaScript is often loosely described as an "interpreted" language, but modern engines like V8 actually use a hybrid approach called JIT (Just-In-Time) compilation. The engine parses your code into an intermediate form, generates bytecode, starts executing it quickly, and simultaneously watches which parts of the code run often ("hot" code) so it can recompile those specific parts into optimized machine code on the fly.
As a working mental model at this stage, it is enough to know: your source text is parsed, then converted into a form the CPU can execute, and this happens automatically and very quickly every time the code runs — you do not need a separate manual "build" step the way some other languages require, though modern tooling sometimes still adds one for developer convenience (like transpiling or bundling).
Execution contexts and the call stack
Whenever JavaScript runs code, it does so inside an execution context — an internal environment that tracks the variables, functions, and the value of this available at that point in the code. There is always a global execution context created first (representing the top level of your script), and a new execution context is created every time a function is called.
These execution contexts are tracked using a call stack, a simple "last in, first out" structure: when a function is called, a new context is pushed onto the top of the stack; when that function finishes (returns), its context is popped off. This is why, if a function calls itself endlessly without stopping, you eventually see a "Maximum call stack size exceeded" error — the stack has run out of room.
Memory
As your code runs, values need somewhere to live — this is memory. JavaScript engines allocate memory automatically when you create variables, objects, arrays, and functions, and they automatically reclaim (free) that memory once it is no longer reachable, through a background process called garbage collection.
This automatic memory management is a major convenience compared to lower-level languages where a programmer must manually allocate and free memory, but it also means understanding that keeping unnecessary references to large objects (for example, in a growing array you never clear) can prevent memory from being freed and cause a program to use more and more memory over time — commonly called a memory leak.
Browser APIs: JavaScript's toolbox in the browser
The core JavaScript language (as defined by ECMAScript) is deliberately minimal — it has no built-in way to change what's on screen, make a network request, or wait for a timer. Those abilities come from Browser APIs (also called Web APIs): extra functionality that the browser environment provides alongside the engine, such as the DOM API (document), timers (setTimeout, setInterval), and networking (fetch).
These APIs are not part of the JavaScript language itself — Node.js provides a different set of APIs for its own environment (like file system access) instead of DOM APIs, because Node.js has no web page to manipulate. This is why the same JavaScript language can look quite different depending on where it runs.
Code Examples
This traces how the call stack grows and shrinks: calling main() pushes its context, calling greet() from inside it pushes another context on top, and each finishes (pops) in the reverse order it was pushed. Expected result: Hello! Done. Stack order: [global] → [global, main] → [global, main, greet] → [global, main] (greet finishes) → [global] (main finishes).
greet(); is called from inside main(), so main()'s context stays on the stack underneath greet()'s until greet() finishes.
This demonstrates how a browser API (setTimeout) interacts with the call stack: even with a delay of 0 milliseconds, the callback does not run immediately — it is handed off to the browser, and only runs after the current script has fully finished and the call stack is empty. Expected result: Start End This runs later setTimeout(..., 0) still runs after "End" is logged, because JavaScript finishes its current stack of synchronous code first.
Common mistake: A common beginner assumption is that setTimeout(fn, 0) runs "immediately" — it actually always waits for the current call stack to empty first, which is why "End" logs before it.
function greet() {
console.log("Hello!");
}
function main() {
greet();
console.log("Done.");
}
main();console.log("Start");
setTimeout(function () {
console.log("This runs later");
}, 0);
console.log("End");From source code to running behavior
JavaScript source code is parsed, compiled just-in-time, and executed inside execution contexts tracked by the call stack, with browser APIs supplying capabilities outside the core language.
SOURCE CODE (.js text)
│
▼
PARSING ──► SyntaxError if invalid
│
▼
JIT COMPILATION (bytecode → optimized machine code)
│
▼
EXECUTION CONTEXT created ──► pushed onto CALL STACK
│
▼
runs, using MEMORY for variables/objects
│
▼
needs browser features? ──► calls a BROWSER API
│ (DOM, setTimeout, fetch, ...)
▼
context finishes ──► popped off CALL STACKImportant Rules
Code must fully parse without a syntax error before any of it can run.
A new execution context is created for the global scope and for every function call.
The call stack tracks active execution contexts in last-in, first-out order.
Memory for variables and objects is managed automatically by the engine's garbage collector.
Capabilities like the DOM, timers, and network requests come from browser APIs, not from the core JavaScript language itself.
Where this shows up
Understanding the call stack is essential for reading error stack traces (which list, in order, which function called which), and understanding browser APIs vs the core language is essential once you start learning asynchronous JavaScript (fetch, promises, async/await) later in the course.
[Predict the Output] What does the call stack error "Maximum call stack size exceeded" indicate? A. The browser has run out of network bandwidth B. A function (often recursive) kept calling itself without ever returning, filling up the call stack C. A variable was declared without a value D. The HTML file is too large to parse
B. A function (often recursive) kept calling itself without ever returning, filling up the call stack — The call stack has a finite size; a function that keeps calling itself (directly or indirectly) without a stopping condition keeps pushing new execution contexts until the stack overflows.
[Multiple Choice] Which of the following is provided by a Browser API rather than the core JavaScript (ECMAScript) language? A. if/else statements B. setTimeout() C. let and const D. arithmetic operators like +
B. setTimeout() — setTimeout is a Web/Browser API supplied by the environment, not a feature defined by the ECMAScript language specification itself.
Without running it, predict the order of console.log output for this code, then verify: console.log('A'); setTimeout(() => console.log('B'), 0); console.log('C');
// Predicted and actual output: // A // C // B Remember that setTimeout hands its callback to the browser, which only runs it after the current synchronous code finishes.