JS Sketchbooksee JavaScript think ✏️
← back to phase 5

5.1 — Execution contexts: the two passes

Here's a small impossibility: line 1 of today's code calls a function that isn't written until line 3 — and it works. If code truly ran top-to-bottom, line 1 should crash with "sayHi is not defined." It doesn't. Which means the story you've had since lesson 0.3 — code runs top to bottom — is missing a chapter.

The missing chapter: the engine reads your code TWICE. Pass one — the creation phase — sweeps the scope and registers every declaration into a memory table. Pass two — the execution phase — actually runs the lines.

The table plus the code being run is called an execution context, and it is THE concept this whole phase is built on: hoisting, scope chain, closures-done-precisely, even this all live here.

watch it happen
sayHi();

function sayHi() {
  console.log("hi!");
}

console.log(score);
var score = 10;
console.log(score);

Before executing a single line, the engine builds ONE EXECUTION CONTEXT for this scope: a memory table for its names, plus the code to run. Every scope — global, every function call — gets one.

the programsayHi(); function sayHi() { console.log("hi!");} console.log(score);var score = 10;console.log(score);about to start…global context — registered names(empty)every scope gets ONE execution context: a memory table for itsnames, plus the code to runconsole(nothing printed yet)
under the hood

The deeper story, with the real names for things — this part is what turns “I saw it” into “I can explain it.”

Precision matters here: nothing physically moves in your file. "Hoisting" describes the effect of the creation phase — names are registered before any line runs, so they behave as if declared at the top.

What differs per declaration kind (var / let / const / function) is how they're registered — that's the entire next lesson.

The execution context is the phase's master picture. Each holds its memory table, its outer-scope reference (lesson 5.3's chain), and — for function calls — its own this (lesson 5.4).

The call stack from 3.6 is, precisely, a stack of execution contexts. One concept, four lessons of payoff.

Fun fact: you use two-pass reading too. Skim a recipe before cooking — "ah, there's an oven step, a marinade, a sauce" — then execute it step by step. A cook who starts frying at word one discovers "marinate overnight" too late; the skim is your creation phase.

your turn

⌨️ prove the two passes

Write a program that could only work because of pass 1 — a function called before its definition, and a var read before its assignment. You’re not fixing this behavior; you’re demonstrating you can predict it.

requirements:

  • First line of your program: print banner — a var that is declared further down. It must print undefined (and you must know why).
  • Then call setBanner() — a function DECLARATION written at the very bottom of the file — which assigns "Grand Opening!" to banner.
  • Print banner again, then place the var declaration and the function declaration at the bottom.

when you press RUN, the console must show exactly:

undefined
Grand Opening!

✏️ Quick check 1

Type exactly what this prints:

greet();

function greet() {
  console.log("yo");
}

✏️ Quick check 2

Type exactly what this prints:

console.log(x);
var x = 5;

✏️ Quick check 3

The engine’s first sweep — where declarations are registered before anything runs — is called the ___ phase. Type the word.

teach it back

🗣️ Now teach it back

A friend is baffled that calling a function above its definition works, but reading a var above its line gives undefined. Explain the two passes — and why the two cases behave differently.

Write it as if your friend is sitting next to you. Saved to your journal — future-you will use these notes to teach others.

a few sentences, minimum — you’ve got this
to remember
Every scope runs in TWO passes: creation (register declarations into the context’s memory) then execution (run lines top to bottom).
Function declarations register COMPLETE (callable before their line); var registers the name with undefined (assignment happens in pass 2).
The memory table + running code = an EXECUTION CONTEXT; each function call gets its own. The 3.6 call stack is a stack of these.
next: 5.2 Hoisting, demystified →