# JavaScript From Zero

> Learn JavaScript from nothing to genuinely advanced: where it runs, values and types, collections, control flow, modules, async and the DOM - then the deep half: scope and closures, this and prototypes, generators, the event loop, functional JS, bundlers, performance, and the road to TypeScript. Small, runnable steps, clear explanations.


---

# JavaScript From Zero

JavaScript is the one language you can't avoid. It runs every website on Earth, it runs on servers
through Node.js, it powers desktop apps and phone apps and build tools. That ubiquity is a blessing and a
curse: there's a *lot* of it, and most tutorials drop you straight into a framework without ever
explaining what the language actually is or how it thinks.

This guide does the opposite. We build your mental model first - what a value is, how types behave, why
`let` and `const` replaced `var`, what "asynchronous" really means - and only then reach for the tools.
By the end you'll be able to *reason* about JavaScript instead of pasting snippets and praying.

It's one zero-to-hero journey in two halves. **Phases 1–9 are the basics** - enough to write real,
well-organized programs and read any codebase. **Phases 10–17 are the deep half** - scope and closures,
`this` and prototypes, generators, the event loop's true ordering, functional JS, bundlers, performance,
and the road to TypeScript. That's the stuff that separates "writes JavaScript" from "understands
JavaScript." Each phase carries a difficulty badge so you can see the climb.

> 📝 This guide teaches the **language**. If you've never programmed at all, start with
> [Programming From Zero](/guides/programming-from-zero) first - it covers the universal ideas (what a
> program is, variables, loops) that every language shares. Then come back here.

## How to read this

- **Brand new to JavaScript?** Read 1–9 in order, top to bottom - each builds on the last. Type the
  examples out yourself (most are runnable right here); running code teaches more than reading it five
  times. Come back for 10+ when the basics feel comfortable.
- **Already know another language?** Skim phases 1–5 for the JavaScript-specific details (loose typing,
  `===`, ES modules, `package.json`), then slow down at phase 6 - the asynchronous model is where
  JavaScript genuinely differs from most languages.
- **Past the basics already?** Jump straight to the deep half - [Phase 10: Scope, Closures &
  Hoisting](10-scope-and-closures.md) onward is where JavaScript stops being "a language you can use" and
  becomes one you can reason about to the metal.

## The phases

**Part 1 - The basics (🟢 Basic → 🟡 Intermediate)**
1. **[Install & Your First Program](01-install-and-first-program.md)** 🟢 - the browser and Node.js; install Node, run a file, use the console.
2. **[Syntax, Values & Types](02-syntax-values-and-types.md)** 🟢 - `let`/`const`, the primitive types, template literals, loose typing.
3. **[Collections](03-collections.md)** 🟢 - arrays and objects, daily methods, and the reference-vs-value trap.
4. **[Control Flow & Functions](04-control-flow-and-functions.md)** 🟢 - `if`/loops/functions, arrow functions, functions as values.
5. **[Modules & Project Layout](05-modules-and-project-layout.md)** 🟢 - `import`/`export`, `package.json`, `node_modules`, a sane shape.
6. **[Async & the DOM](06-async-and-the-dom.md)** 🟡 - callbacks, promises, `async`/`await`, and reaching into a web page.
7. **[Errors & I/O](07-errors-and-io.md)** 🟡 - `try`/`catch`, files in Node, and the network without falling over.
8. **[Ecosystem & Tooling](08-ecosystem-and-tooling.md)** 🟡 - npm, linters, formatters, bundlers, and where TypeScript fits.
9. **[Idioms & Gotchas](09-idioms-and-gotchas.md)** 🟡 - the fluent patterns, and the famous footguns explained once, properly.

**Part 2 - Beyond the basics (🟡 Intermediate → 🔴 Advanced)**
10. **[Scope, Closures & Hoisting](10-scope-and-closures.md)** 🔴 - the scope chain, the TDZ, and how a function remembers where it was born.
11. **[this, Prototypes & the Object Model](11-this-prototypes-and-objects.md)** 🔴 - the four `this` rules, the prototype chain, classes as sugar.
12. **[Iterators, Generators & Symbols](12-iterators-generators-symbols.md)** 🟡 - the iterable protocol, `function*`, and values on demand.
13. **[The Event Loop, Deep](13-the-event-loop-deep.md)** 🔴 - tasks vs microtasks, and why `Promise` runs before `setTimeout`.
14. **[Functional JavaScript](14-functional-javascript.md)** 🟡 - pure functions, higher-order functions, immutability, composition.
15. **[Modules & Bundlers, Deep](15-modules-and-bundlers.md)** 🟡 - ESM vs CommonJS, tree-shaking, dynamic import, what a bundler does.
16. **[Performance & Memory](16-performance-and-memory.md)** 🔴 - how V8 runs your code, the GC, and finding the real slow thing.
17. **[Types & the Road to TypeScript](17-types-and-typescript.md)** 🟡 - what static typing buys you, and the short leap to TS.

**Finale**
18. **[Where to Go Next](18-where-to-go-next.md)** 🟢 - frameworks, backend, full-stack, and what to actually build.

> Frameworks (React, Vue, Svelte) and TypeScript are their own guides - different tools, not "more
> JavaScript." This guide makes the *language* make sense, top to bottom.


---

# Install & Your First Program

One idea saves a lot of confusion before you write any JavaScript: **the same language runs in two
completely different places, and they don't have the same tools.** Get this straight now and a whole
category of "why doesn't this work?" disappears.

## The mental model: a language vs. a place to run it

JavaScript is just a language - grammar and rules for writing instructions. By itself it doesn't *do*
anything; it needs a program to read and carry out those instructions. That program is called a
**runtime** (or *engine*).

There are two runtimes you'll meet first:

- **The browser** (Chrome, Firefox, Safari, Edge). Every browser has a JavaScript engine built in - this
  is JavaScript's original home, and it exists to make web pages interactive. Your code can touch the
  page: change text, respond to clicks, draw things.
- **Node.js** - a runtime that takes the browser's JavaScript engine and runs it on your computer
  *outside* any web page, so JavaScript can do what a normal program does: read files, run a web server,
  work with your file system.

```mermaid
flowchart LR
  JS["JavaScript<br/>(the language)"] --> B["Browser runtime<br/>web pages, clicks"]
  JS --> N["Node.js runtime<br/>files, servers"]
```

📝 **Terminology.** A **runtime** is the program that executes your JavaScript. Same language, different
runtime, different powers.

⚠️ **The two are not interchangeable.** The browser has objects like `window` and `document` (the web
page) that **do not exist in Node**. Node has tools like `fs` (the file system) that **do not exist in
the browser** - a web page on a stranger's site has no business reading your hard drive. Copy browser
code into Node and see `document is not defined`? Not a bug - that tool doesn't exist there.

## Install Node.js

You'll do most of your early learning in Node, since running a file from the terminal is the simplest loop
there is.

**The simple way:** go to [nodejs.org](https://nodejs.org) and download the **LTS** version ("Long Term
Support" - the stable one most projects use). Run the installer and accept the defaults.

**The flexible way (recommended once you're comfortable):** install [nvm](https://github.com/nvm-sh/nvm)
("Node Version Manager") to install and switch between multiple Node versions - real projects often pin a
specific version, and nvm makes that painless. On Windows, use
[nvm-windows](https://github.com/coreybutler/nvm-windows).

Either way, confirm it worked by asking Node its version:
```console
$ node --version
v24.11.0
```
*What just happened:* `node --version` printed the installed version. The exact number will differ - what
matters is a `v` and some numbers, not `command not found`. Getting that error means Node isn't on your
PATH yet; reopening your terminal (or rebooting) usually fixes a fresh install.

📝 **Terminology.** A **flag** is an extra option passed to a command, usually starting with `--` (or a
single `-`). `--version` means "tell me your version and exit."

## Your first program: a file Node runs

Create a file called `hello.js` (`.js` is the convention for JavaScript files) with one line in it:
```javascript runnable
console.log("Hello, JavaScript!");
```
*What just happened:* `console.log` prints its argument to the output. (It works in *both* runtimes - in
Node it prints to your terminal, in the browser to the browser's console. One of the few tools both share.)

Now run the file with Node:
```console
$ node hello.js
Hello, JavaScript!
```
*What just happened:* Node read the file top to bottom, ran the one instruction, and printed the result.
That's the loop you'll repeat thousands of times: **edit a `.js` file, run it with `node`, read the
output.**

💡 **Key point.** `node hello.js` means "Node, run this file." Nothing to compile, no build button - Node
reads your source directly and runs it.

## The other runtime: the browser console

Running JavaScript in a browser needs no install - it's already there. Open any web page, then open the
**developer console**:

- **Chrome / Edge:** press `F12`, or `Ctrl+Shift+J` (Windows/Linux) / `Cmd+Option+J` (Mac).
- **Firefox:** press `F12`, or `Ctrl+Shift+K` (Windows/Linux) / `Cmd+Option+K` (Mac).
- Click the **Console** tab.

Type JavaScript at the prompt and press Enter to run it immediately:
```javascript
console.log("Hello from the browser!");
2 + 2
```
*What just happened:* The console is a live, one-line-at-a-time runtime. `console.log` printed your text;
the bare line `2 + 2` got *evaluated* and the console echoed its result, `4`, automatically - a great
scratchpad. (A `.js` file run by Node shows no output for a bare `2 + 2`; that's a console convenience,
not a language feature.)

🪖 **War story.** Nearly every JavaScript developer has opened the browser console expecting Node behavior
and gotten a confusing result - or pasted Node code into the console and watched it complain that `require`
or `fs` doesn't exist. It's the two-runtimes thing again. When code surprises you, ask: *which runtime am I
in, and does it have the tool I just used?*

## Recap

1. **JavaScript is a language; a runtime is what runs it.** The two you start with are the **browser** and
   **Node.js**.
2. **The runtimes have different tools.** The browser has `window`/`document`; Node has `fs`. Code written
   for one can fail in the other - that's expected, not broken.
3. **Install Node** from nodejs.org (LTS) or via nvm, and confirm with `node --version`.
4. **Run a file** with `node hello.js`; `console.log(...)` prints output in both runtimes.
5. **The browser console** is a live JavaScript scratchpad - open it with `F12` and the Console tab.

Next: values, the types they come in, and the ways to name and combine them.


---

# Syntax, Values & Types

A program shuffles **values** around - text, numbers, true/false - and needs to *name* them to refer to
later. This phase covers both: the values JavaScript has, and how you name them. Most of JavaScript's
famous "weird parts" live here, so we'll meet them head-on rather than let them ambush you.

## Naming values: `let` and `const`

A variable is a name pointing at a value. Make one with `let` or `const`, an `=`, and the value:
```javascript runnable
let score = 0;
const name = "Ada";
console.log(score, name);
```
```console
0 Ada
```
*What just happened:* `let score = 0` created `score` pointing at `0`; `const name = "Ada"` pointed
`name` at `"Ada"`. The semicolon `;` ends a statement - JavaScript is fairly relaxed about them, but
using them is a good habit that avoids a rare class of surprises.

One rule tells them apart:

- **`let`** - reassignable. Use for values that change (a score, a counter, a running total).
- **`const`** - *not* reassignable. Use for values that shouldn't change. The one you'll use most.

```javascript runnable
let count = 1;
count = 2;          // fine - let allows this
const limit = 10;
limit = 20;         // error - const forbids reassignment
```
```console
TypeError: Assignment to constant variable.
```
*What just happened:* Reassigning `count` worked since it's a `let`; reassigning `limit` threw a
`TypeError`, because `const` means "this name will always point at this value" - a safety rail, not a
restriction to fight.

💡 **Key point.** Default to `const`; reach for `let` only when you genuinely need to reassign. Code
where most names are `const` is easier to reason about.

⚠️ **Why not `var`?** The original way to declare variables, with two traps `let`/`const` were designed
to fix: its scope leaks out of blocks in surprising ways, and it lets you redeclare the same name
silently. The community moved on years ago (2015, "ES2015"). Read `var` when you see it; don't write it.

## The primitive types

Every value has a **type** that determines what it is and what you can do with it. The fundamental
("primitive") types you'll use constantly:

- **string** - text: `"hello"`, `'also hello'`. Single or double quotes work; pick one and stay
  consistent.
- **number** - any number, whole or decimal: `42`, `3.14`, `-7`. JavaScript has *one* number type, not
  separate integer and float types.
- **boolean** - `true` or `false`, the answer to a yes/no question.
- **null** - a deliberate "nothing here"; *you* set something to `null` to mean "intentionally empty."
- **undefined** - "no value assigned yet," which JavaScript hands you automatically for a variable that
  exists but was never given a value.

You can ask any value its type with the `typeof` operator:
```javascript runnable
console.log(typeof "hello");   // string
console.log(typeof 42);        // number
console.log(typeof true);      // boolean
console.log(typeof undefined); // undefined
```
```console
string
number
boolean
undefined
```
*What just happened:* `typeof` reports the type of the value to its right as a string - handy when you're
unsure what you're holding, especially for values from outside your control.

📝 **Terminology - `null` vs `undefined`.** Both mean "no real value," which confuses everyone at first:
**`undefined` is the system's "you never set this"**; **`null` is your "I'm deliberately setting it to
empty."** An unfilled search box might be `""` (empty string); a setting you explicitly cleared might be
`null`; a variable you declared but didn't assign is `undefined`.

## Template literals: building strings cleanly

You'll constantly want to mix text and values. The clean way uses **template literals** - strings wrapped
in backticks (`` ` ``) instead of quotes, with `${...}` holes you drop values into:
```javascript runnable
const name = "Ada";
const score = 42;
console.log(`${name} scored ${score} points.`);
```
```console
Ada scored 42 points.
```
*What just happened:* Inside backticks, anything in `${...}` gets evaluated and dropped into the string.
No fiddly `+` between pieces, no quote-juggling. Template literals also let a string span multiple lines
without tricks - once you meet them you'll rarely glue strings together any other way.

## Dynamic and loose typing - the part to understand deeply

This is where JavaScript surprises people from other languages - two separate ideas that often get
muddled.

**Dynamic typing** means a variable's type isn't fixed - it's decided by whatever value is in it right
now, and it can change:
```javascript
let x = 42;        // x holds a number
x = "now text";    // perfectly legal - x now holds a string
```
*What just happened:* The same name `x` held a number, then a string. JavaScript never made you *declare*
a type; the value carries its own type, and the variable just points at whatever's there. Flexible, and
also a source of bugs - nothing stops a variable from quietly becoming a different kind of thing.

**Loose typing** means JavaScript *automatically converts* between types when an operation mixes them,
unasked. The famous footgun:
```javascript runnable
console.log("5" + 1);   // string + number
console.log("5" - 1);   // string - number
```
```console
51
4
```
*What just happened:* With `+`, JavaScript saw a string on the left, decided you must mean
*concatenation*, turned `1` into `"1"`, and glued them: `"51"`. With `-`, there's no string version of
subtraction, so it went the other way - turned `"5"` into the number `5` and subtracted: `4`. Same two
values, opposite conversions, depending on the operator - loose typing in a nutshell.

## ⚠️ The gotchas everyone hits

**`==` vs `===` - always use `===`.** JavaScript has two equality operators: `==` ("loose equality")
converts types before comparing, producing baffling results; `===` ("strict equality") compares without
converting - values must match in *both* value and type.
```javascript runnable
console.log(0 == "");      // true  ("" converts to the number 0)
console.log(0 === "");     // false (number vs string - no conversion)
console.log(1 == "1");     // true  ("1" converted to 1)
console.log(1 === "1");    // false (number vs string)
```
*What just happened:* `==` quietly converted types to find a match, producing surprises like `0 == ""`
being `true`. `===` refused to convert, so a number and a string are never equal. Make it a habit: **use
`===` (and `!==`) everywhere.** The only exception - checking for `null`/`undefined` together - you can
learn later.

**`NaN` - "Not a Number," and it's contagious.** When a math operation can't produce a real number, you
get `NaN`:
```javascript runnable
console.log(Number("hello"));   // tried to make a number from non-numeric text
console.log(NaN === NaN);       // the famous one
```
```console
NaN
false
```
*What just happened:* `Number("hello")` couldn't find a number in `"hello"`, so it returned `NaN`. And
`NaN === NaN` is `false` - `NaN` is the only value not equal to itself, by design (it represents "an
invalid result," and two invalid results aren't meaningfully "the same"). To check for it, use
`Number.isNaN(x)`, never `x === NaN`.

**Floating-point money - don't store cents as decimals.** JavaScript numbers can't represent every decimal
exactly, the single most reported "JavaScript is broken" moment:
```javascript runnable
console.log(0.1 + 0.2);
console.log(0.1 + 0.2 === 0.3);
```
```console
0.30000000000000004
false
```
*What just happened:* `0.1` and `0.2` can't be stored exactly in the binary format JavaScript uses for
numbers (true in almost every language, not a JavaScript flaw), so their sum is a hair off `0.3`. The
fix: **work in the smallest unit as whole numbers** - store `$1.30` as `130` cents, do math on integers,
divide by 100 only when displaying. Never compare prices with `===` on decimals.

## Recap

1. **`const` by default, `let` when you must reassign.** Avoid `var` - read it, don't write it.
2. **Primitive types:** string, number (one type for all numbers), boolean, `null` (deliberate empty),
   `undefined` (never assigned). Check with `typeof`.
3. **Template literals** (`` `${value}` ``) are the clean way to mix text and values.
4. **Dynamic typing:** a variable's type follows its current value. **Loose typing:** operations
   auto-convert across types - sometimes helpfully, often confusingly.
5. **Always use `===`**, check `NaN` with `Number.isNaN`, never trust `===` on decimal money - work in
   integer cents.

Next: *collections* of values - lists (arrays) and labeled bundles (objects).


---

# Collections - Arrays & Objects

Real programs deal with *many* things - a list of users, the fields of a form, the items in a cart.
JavaScript has two workhorse collections for this: the **array** (an ordered list) and the **object** (a
labeled bundle). Get these two fluent and a huge amount of JavaScript opens up.

## Arrays: ordered lists

An array is an ordered list of values, written with square brackets. Values can be any type, and you
reach into the list by *position* - counting from **0**, not 1.
```javascript runnable
const fruits = ["apple", "banana", "cherry"];
console.log(fruits[0]);     // first item
console.log(fruits[2]);     // third item
console.log(fruits.length); // how many items
```
```console
apple
cherry
3
```
*What just happened:* `fruits[0]` is the first element, since array indexes start at zero - universal
across most languages. `fruits[2]` is the third. `.length` gives the item count. A nonexistent index
(`fruits[99]`) gives `undefined`, not an error.

You can add and change items:
```javascript runnable
const fruits = ["apple", "banana"];
fruits.push("cherry");   // add to the end
fruits[0] = "apricot";   // replace the first item
console.log(fruits);
```
```console
[ 'apricot', 'banana', 'cherry' ]
```
*What just happened:* `.push(...)` appended `"cherry"` to the end; `fruits[0] = "apricot"` overwrote the
first slot. The array is a `const` and yet we changed its *contents* - allowed, for a reason that's the
most important idea in this phase (more below).

### A taste of array methods: `map`, `filter`, `reduce`

Arrays come with built-in methods that transform lists without manual loops. These three you'll reach for
constantly, so meet them now even if they feel like a lot at first.

**`map`** makes a *new* array by transforming every item:
```javascript runnable
const numbers = [1, 2, 3];
const doubled = numbers.map((n) => n * 2);
console.log(doubled);
```
```console
[ 2, 4, 6 ]
```
*What just happened:* `.map(...)` walked `numbers`, ran `(n) => n * 2` on each item, and collected the
results into a brand-new array. (That's an **arrow function** - covered properly in
[Phase 4](04-control-flow-and-functions.md); for now read it as "given `n`, give back `n * 2`.") The
original `numbers` is untouched.

**`filter`** makes a new array keeping only the items that pass a test:
```javascript runnable
const numbers = [1, 2, 3, 4, 5, 6];
const evens = numbers.filter((n) => n % 2 === 0);
console.log(evens);
```
```console
[ 2, 4, 6 ]
```
*What just happened:* `.filter(...)` kept each item only when `n % 2 === 0` ("remainder on dividing by 2
is zero," i.e. even) returned `true`; the odd numbers were dropped. Again, a new array comes out and the
original stays put.

**`reduce`** boils a whole array down to a single value:
```javascript runnable
const numbers = [10, 20, 30];
const total = numbers.reduce((sum, n) => sum + n, 0);
console.log(total);
```
```console
60
```
*What just happened:* `.reduce(...)` carries a running value (`sum`) across the list, starting at `0`
(the second argument), adding each item on: `0+10`, `+20`, `+30`, landing on `60`. Most powerful and
least obvious of the three - mental model: "fold the list into one result, one item at a time."

💡 **Key point.** `map`, `filter`, and `reduce` all return *new* values and leave the original array
alone. Building new data instead of mutating old data prevents a whole class of bugs, and it reads like a
sentence: "take the numbers, *filter* the evens, *map* them doubled."

## Objects: labeled bundles

Where an array holds values by *position*, an object holds values by *name* - a bundle of `key: value`
pairs in curly braces, perfect for representing one "thing" with several properties.
```javascript runnable
const user = {
  name: "Ada",
  age: 36,
  isAdmin: true,
};
console.log(user.name);     // dot notation
console.log(user["age"]);   // bracket notation
```
```console
Ada
36
```
*What just happened:* `user` bundles three labeled values, read by key either with a dot (`user.name` -
what you'll use most) or with brackets and the key as a string (`user["age"]` - needed when the key is in
a variable or has unusual characters).

You change and add properties freely:
```javascript runnable
const user = { name: "Ada" };
user.age = 36;        // add a new property
user.name = "Ada L."; // change an existing one
console.log(user);
```
```console
{ name: 'Ada L.', age: 36 }
```
*What just happened:* Assigning to `user.age` (a key that didn't exist) *added* it; assigning to
`user.name` *changed* it. Objects are open for extension by default.

📝 **Terminology.** A **property** is one `key: value` pair on an object; a **method** is a property
whose value is a function (e.g. `console.log` is the `log` method of the `console` object). Arrays are
technically a special kind of object too - why they have methods like `.push()`.

## A one-line note on `Map` and `Set`

For most "labeled data," a plain object is exactly right. But JavaScript also has two purpose-built
collections worth *knowing the names of*: a **`Map`** is like an object but its keys can be *any* type
(not just strings) and it remembers insertion order cleanly; a **`Set`** is a list that automatically
rejects duplicates. Reach for them when those powers matter; until then, arrays and objects cover most
real code.

## ⚠️ The big one: reference vs. value

This single idea explains the `const`-but-still-changeable puzzle from earlier, *and* a bug that bites
every JavaScript developer. Pay attention here.

**Primitives are copied by value. Objects and arrays are shared by reference.** Assign a number or string
and you copy the value. Assign an object or array and you copy a *reference* - a pointer to the same
underlying thing. Two names, one object.
```javascript runnable
const a = { count: 1 };
const b = a;        // b points at the SAME object as a
b.count = 99;
console.log(a.count);
```
```console
99
```
*What just happened:* `const b = a` did **not** make a second object - it made `b` point at the *exact
same* object `a` points at, so changing `b.count` also changed `a.count`. This surprises everyone the
first time. (Compare with primitives: `let x = 1; let y = x; y = 99;` leaves `x` as `1`, because the
number was copied.)

This is also why a `const` array can still be `push`ed into: `const` locks the *name* to one object, but
the object's *insides* stay free to change. `const` protects the pointer, not the contents.

And it's why two equal-looking objects aren't equal:
```javascript runnable
console.log({ x: 1 } === { x: 1 });   // two separate objects
const same = { x: 1 };
console.log(same === same);           // the same object
```
```console
false
true
```
*What just happened:* The first comparison is `false` because those are *two different objects* that
merely *look* alike - `===` on objects asks "are these the same object?", not "do they contain the same
stuff?" The second is `true` because both sides are literally the same object. To compare *contents*,
compare the fields yourself (or use a library) - source of countless "but they're the same!" debugging
sessions.

🪖 **War story.** A classic bug: copy an array with `const copy = original`, tweak `copy`, and later
discover `original` changed too - they were always the same array. The fix: make a *real* copy with
`const copy = [...original]` (array) or `const copy = { ...original }` (object). That `...` is the
"spread" syntax; it builds a new collection with the old one's items shallow-copied in. Keep it in your
back pocket.

## Recap

1. **Arrays** are ordered lists indexed from **0**; `.length` counts them, `.push()` appends.
2. **`map`/`filter`/`reduce`** transform / select / fold an array into a *new* value, leaving the original
   alone.
3. **Objects** are `key: value` bundles read with `.dot` or `["bracket"]` notation; properties can be
   added and changed freely.
4. **`Map`** (any-type keys) and **`Set`** (no duplicates) exist for special cases - know the names.
5. **Reference vs. value:** objects and arrays are *shared*, not copied, on assignment. This explains
   `const` arrays you can still mutate, and why `{x:1} === {x:1}` is `false`. Copy with `[...a]` / `{...o}`.

Next: control flow - the logic that decides *which* code runs and *how often* - and functions, which let
you name and reuse blocks of behavior.


---

# Control Flow & Functions

So far your code runs straight down, every line, once. Real programs need to *choose* (do this only if
that's true), *repeat* (do this for every item), and *reuse* behavior without copy-pasting. Those three
needs - decisions, loops, and functions - are this phase. Functions especially: the unit you'll think in
for the rest of your career.

## Making decisions: `if` / `else`

`if` runs a block of code only when a condition is `true`; `else` covers the other case.
```javascript runnable
const hour = 14;

if (hour < 12) {
  console.log("Good morning");
} else if (hour < 18) {
  console.log("Good afternoon");
} else {
  console.log("Good evening");
}
```
```console
Good afternoon
```
*What just happened:* JavaScript checked the conditions top to bottom. `hour < 12` was `false` (14 isn't
less than 12), so it moved on; `hour < 18` was `true`, so it ran *that* block and skipped the rest. The
first matching branch wins - `{ }` braces group the lines in each branch.

Here's that decision as a picture:

```mermaid
flowchart TD
  A{hour < 12?} -->|yes| M[Good morning]
  A -->|no| B{hour < 18?}
  B -->|yes| AF[Good afternoon]
  B -->|no| E[Good evening]
```

📝 **Terminology - truthy and falsy.** Conditions don't have to be literal `true`/`false` - JavaScript
treats some values as "truthy," others "falsy," in a yes/no context. Falsy values are worth memorizing
since they're a common source of bugs: `false`, `0`, `""` (empty string), `null`, `undefined`, `NaN`.
*Everything else* is truthy - including `"0"` (a non-empty string) and `[]` (an empty array). So
`if (value)` means "if value is truthy."

## Repeating: `for...of` and `while`

The cleanest way to do something with every item in an array is `for...of`:
```javascript runnable
const names = ["Ada", "Linus", "Grace"];
for (const name of names) {
  console.log(`Hello, ${name}`);
}
```
```console
Hello, Ada
Hello, Linus
Hello, Grace
```
*What just happened:* `for (const name of names)` walked `names`, putting each item into `name` and
running the block - no index counter to manage. (You'll also see the older C-style
`for (let i = 0; i < names.length; i++)` loop; `for...of` is cleaner when you don't need the index.)

When you don't know how many times to loop in advance, use `while` - it repeats *as long as* its
condition stays true:
```javascript runnable
let countdown = 3;
while (countdown > 0) {
  console.log(countdown);
  countdown = countdown - 1;
}
console.log("Liftoff!");
```
```console
3
2
1
Liftoff!
```
*What just happened:* `while` checked `countdown > 0`, ran the block, and checked again, repeating until
`countdown` hit `0`. Crucially, the block *changes* `countdown` each time. ⚠️ A `while` loop whose
condition never becomes false (you forget to change the thing it checks) runs forever and freezes your
program - the classic "infinite loop." Make sure each pass moves toward the exit.

## Functions: naming a block of behavior

A function is a named, reusable block of instructions taking *inputs* (parameters) and handing back an
*output* (a return value) - write behavior once, run it whenever you need it, with different inputs.
```javascript runnable
function greet(name) {
  return `Hello, ${name}!`;
}

console.log(greet("Ada"));
console.log(greet("Grace"));
```
```console
Hello, Ada!
Hello, Grace!
```
*What just happened:* `function greet(name) { ... }` defined a function with one **parameter**, `name`.
`return` hands a value back to the caller. `greet("Ada")` produced `"Hello, Ada!"`; same function, called
twice with different inputs, gave two different outputs - the whole point of parameters.

📝 **Terminology.** A **parameter** is the name in the function definition (`name`); an **argument** is
the value passed in when calling (`"Ada"`). `return` ends the function and sends a value back; a function
with no `return` hands back `undefined`.

**Default parameters** let a parameter fall back to a value if the caller leaves it out:
```javascript runnable
function greet(name = "friend") {
  return `Hello, ${name}!`;
}
console.log(greet());          // no argument passed
console.log(greet("Ada"));
```
```console
Hello, friend!
Hello, Ada!
```
*What just happened:* Calling `greet()` with no argument let `name` fall back to its default, `"friend"`;
passing `"Ada"` overrode it. Defaults save you from scattering "if it wasn't provided, use X" checks
through your code.

## Arrow functions: the compact form

You've already seen these in [Phase 3](03-collections.md). An **arrow function** is a shorter way to
write a function, used constantly for small inline functions:
```javascript runnable
const double = (n) => n * 2;
const greet = (name) => `Hello, ${name}!`;

console.log(double(5));
console.log(greet("Ada"));
```
```console
10
Hello, Ada!
```
*What just happened:* `(n) => n * 2` is a function taking `n` and returning `n * 2`. A single-expression
body skips the `{ }` and the word `return` - the value returns automatically. Same idea as a `function`,
written tighter. For a multi-line body, bring back the braces and an explicit `return`:
`(n) => { const r = n * 2; return r; }`.

For now, treat arrow functions and `function` declarations as two ways to write the same thing. One real
behavioral difference around `this` is flagged at the end of this phase and covered fully later.

## Functions are values you can pass around

The idea that makes JavaScript click: **a function is itself a value** - you can store it in a variable,
put it in an array, and (the powerful part) *pass it to another function*. A function passed to another
function is called a **callback**.
```javascript runnable
function runTwice(action) {
  action();
  action();
}

runTwice(() => console.log("tick"));
```
```console
tick
tick
```
*What just happened:* `runTwice` takes a function as its argument and calls it twice. We handed it
`() => console.log("tick")`, and it ran twice. This is exactly what `map`/`filter`/`reduce` do: hand them
a function, and *they* decide when and how to call it on your data. Once this feels natural, huge swaths
of JavaScript (event handlers, array methods, async code) stop looking like magic.

💡 **Key point.** "First-class functions" is the formal name for this: functions are values, equal
citizens with numbers and strings. Passing behavior into other code, not just data, is the backbone of
how JavaScript handles clicks, timers, and network responses - all coming in
[Phase 6](06-async-and-the-dom.md).

## ⚠️ A tease: `this` depends on *how* you call a function

You'll eventually meet the keyword `this` inside functions - one of JavaScript's genuinely confusing
corners. The sentence that defuses most of the pain: **`this` is not set by where a function is
*defined*, but by *how it is called*.** The same function sees a different `this` depending on whether
you call it as a method, on its own, or as a callback. Arrow functions don't get their own `this` - one
reason people prefer them for callbacks.

That's all for now; the full mental model gets its own treatment in
[Phase 9: Idioms & Gotchas](09-idioms-and-gotchas.md).

## Recap

1. **`if` / `else if` / `else`** picks the first branch whose condition is truthy; remember the falsy
   values (`false`, `0`, `""`, `null`, `undefined`, `NaN`).
2. **`for...of`** loops over each item in a list cleanly; **`while`** repeats until its condition goes
   false - make sure it eventually does.
3. **Functions** package reusable behavior: **parameters** are inputs, `return` is the output, and
   **defaults** cover missing arguments.
4. **Arrow functions** (`(n) => n * 2`) are the compact form for small inline functions.
5. **Functions are values** - you can pass them around; one passed into another function is a
   **callback**. And `this` is decided by *how* a function is called, not where it's written.

Next: modules, which let you split a program across files and pull the pieces together cleanly.


---

# Modules & Project Layout

Everything so far lived in one file - fine for ten lines, but it falls apart fast for anything real.
Programs grow, and you want related code grouped into files you can find, reuse, and reason about
separately. The tool for that is **modules**. This phase sets the stage for the async and browser work in
[Phase 6](06-async-and-the-dom.md).

## The mental model: each file is a module

A module is a `.js` file that keeps its contents *private* by default and explicitly *shares* (exports)
the pieces other files may use; other files then *import* exactly what they need. Nothing leaks between
files unless you say so.

This system is called **ES modules** (or "ESM" - the official, modern standard built into the language).
Two keywords run it: `export` to share, `import` to borrow.

📝 **Terminology.** You may also hear about **CommonJS** (`require(...)` / `module.exports`), the *older*
system Node used for years and that you'll still meet in existing code. We teach **ES modules** since
they're the standard going forward and work in both browser and Node. Recognize `require`; write
`import`.

## `export` and `import`

Let's split a tiny program into two files. First, a file that *provides* some helpers:
```javascript
// math.js
export function add(a, b) {
  return a + b;
}

export const PI = 3.14159;
```
*What just happened:* The `export` keyword marks `add` and `PI` as the parts of this file other files may
use. Anything *without* `export` (a helper variable, say) stays private to `math.js`, invisible from
outside. The file is now a reusable module with a clear public surface.

Now a file that *uses* them:
```javascript
// main.js
import { add, PI } from "./math.js";

console.log(add(2, 3));
console.log(PI);
```
```console
5
3.14159
```
*What just happened:* `import { add, PI } from "./math.js"` reached into `math.js` and pulled out the two
exported names. The `./` at the front means "a file right next to me" (a *relative* path), resolving the
same way regardless of which folder you run the program from. ES modules in Node need the `.js`
extension. Run with `node main.js` and Node loads `math.js` automatically because `main.js` asked for it. (For this to run, your project needs the `"type": "module"` setting covered in the next section - or, to skip setup entirely and run right now, name the files `math.mjs`/`main.mjs`: the `.mjs` extension tells Node a file is a module all on its own, with no `package.json`.)

Here's the relationship as a picture - a small **module graph**:

```mermaid
flowchart LR
  main["main.js"] -->|imports add, PI| math["math.js"]
  main -->|imports formatDate| dates["dates.js"]
  math -->|imports round| utils["utils.js"]
```

*Reading it:* arrows point from a file to the files it depends on. `main.js` is the entry point; it pulls
in `math.js` and `dates.js`, and `math.js` pulls in `utils.js`. Node starts at your entry file and follows
these arrows, loading each module once. This graph *is* your program's structure - a clean tree, not a
tangle where everything imports everything, is most of what "good architecture" means here.

### Default exports

A second flavor of export, **default**, is for when a file's main purpose is to provide one thing:
```javascript
// greet.js
export default function greet(name) {
  return `Hello, ${name}!`;
}
```
```javascript
// main.js
import greet from "./greet.js";   // no curly braces for a default
console.log(greet("Ada"));
```
```console
Hello, Ada!
```
*What just happened:* A file can have one `export default`. Import it *without* curly braces and pick any
name for it on the importing side. Use a default when a module is really "about" one thing (a single
function or class); use named `{ ... }` exports when a file offers several helpers. Plenty of code mixes
both.

## What `package.json` is

The moment a project is more than a couple of loose files, it gets a **`package.json`** - a small file at
the project root describing the project. Create one by running:
```console
$ npm init -y
Wrote to /home/ada/my-project/package.json
```
*What just happened:* `npm init -y` created a starter `package.json` with sensible defaults (`-y` says
"yes to all the prompts"). `npm` is Node's package manager, shipped with Node - more in
[Phase 8](08-ecosystem-and-tooling.md). For an ES module project you want it to look roughly like this:
```json
{
  "name": "my-project",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "start": "node main.js"
  },
  "dependencies": {}
}
```
*What just happened:* This file is your project's ID card and control panel. The fields that matter
early:

- **`"type": "module"`** - tells Node to treat your `.js` files as ES modules so `import`/`export` work.
  ⚠️ Without this line, Node assumes the *old* CommonJS system and your `import` statements throw
  `SyntaxError: Cannot use import statement outside a module`. If you hit that error, this missing line
  is almost always why (`npm init -y` doesn't always add it - set it yourself).
- **`"scripts"`** - named shortcuts run with `npm run <name>` (e.g. `npm run start`), saving you retyping
  long commands and documenting how the project is meant to run.
- **`"dependencies"`** - the outside packages your project uses, filled in as you install them.

## What `node_modules` is

Installing an outside package (`npm install some-package`) makes npm download it - and everything *it*
depends on - into a folder called **`node_modules`** at your project root, recording the package in
`package.json`.

⚠️ **`node_modules` is huge and disposable - never commit it.** It can hold thousands of files, and it's
fully rebuildable: anyone with your `package.json` can recreate it via `npm install`. List it in
`.gitignore` and leave it out of version control. The *recipe* (`package.json` and its lockfile) is what
you track; the *downloaded result* (`node_modules`) is not. New developers clone the repo, run
`npm install`, and `node_modules` reappears.

💡 **Key point.** The split is the whole idea: **`package.json` is the recipe you keep; `node_modules` is
the meal you can always re-cook.** Track the recipe, ignore the meal.

## A sane small project layout

You don't need an elaborate structure to start. Here's a layout that scales from tiny to medium without
ceremony:
```text
my-project/
  package.json        the recipe: name, scripts, dependencies
  package-lock.json   exact versions npm installed (commit this)
  .gitignore          lists node_modules/ so Git ignores it
  node_modules/       downloaded packages (ignored, rebuildable)
  src/                your actual code lives here
    main.js           the entry point you run
    math.js           a module of related helpers
    dates.js          another module
  README.md           what this project is and how to run it
```

*Reading it:* the principle is "code in `src/`, config at the root, downloaded stuff ignored." `main.js`
is your entry point - the file you run, the root of the module graph. As the project grows, add more
files under `src/` (and eventually subfolders that group related modules), but the shape stays the same.
Resist inventing structure you don't need yet - let folders appear when the code calls for them.

## Recap

1. **A module is a file** that keeps its contents private and shares only what it `export`s; other files
   pull pieces in with `import`.
2. **Named exports** (`export const x` → `import { x }`) for several helpers; **default export** (one per
   file, no braces on import) when a file is about one thing. Use relative paths with the `.js`
   extension, e.g. `from "./math.js"`.
3. **`package.json`** is your project's recipe - set **`"type": "module"`** so `import`/`export` work,
   define **`scripts`**, and let **`dependencies`** track outside packages.
4. **`node_modules`** holds downloaded packages; huge, rebuildable with `npm install`, **never
   committed** - gitignore it.
5. **A sane layout** keeps code in `src/`, config at the root, `node_modules` ignored - grow it only as
   the code demands.

Next: what makes JavaScript truly distinctive - doing things that take time (network calls, timers,
clicks) without freezing, and reaching into a live web page from your code.


---

# Async & the DOM - JavaScript's Big Idea

By now you can write functions, loop over arrays, and split code into modules. This phase is where JavaScript stops feeling like "a language" and starts feeling like *the thing browsers run* - two ideas define almost everything you'll do with it: **async** (JavaScript does one thing at a time, but refuses to sit and wait for slow things) and the **DOM** (the live, in-memory model of the page that your JavaScript reads and rewrites while the user watches). Get these two, and "make the button fetch some data and update the page" goes from intimidating to obvious.

## The one-thread rule

JavaScript runs your code on a **single thread** - one worker, doing one thing at a time, in order. No second worker quietly runs your other functions in the background; if your code is busy, it's busy, and nothing else of yours runs until it finishes.

That sounds like a recipe for a frozen, useless program. The thing that saves it is the **event loop**.

📝 **Terminology.** A *thread* is a single sequence of execution - one worker, one task at a time. *Asynchronous* ("async") means "started now, finished later" - kick off slow work and get on with other things instead of standing still until it's done.

When your code starts something slow - a network request, a timer, waiting for a click - JavaScript doesn't block the thread waiting for it. It hands the job off (to the browser or Node) and returns immediately. Later, when the slow thing is done, the *follow-up* code drops into a queue, and the event loop runs it once the thread is free.

```mermaid
flowchart LR
  Code[Your code] -->|starts slow work| Off[Hand off to browser/Node]
  Off -->|done later| Queue[Ready queue]
  Queue --> Loop[Event loop]
  Loop -->|thread free| Run[Run the follow-up]
```

*What this shows:* The slow work happens *off* your one thread. The event loop picks the next ready follow-up and runs it when the thread isn't busy - one worker, many things in flight, since waiting doesn't occupy the worker.

⚠️ **Gotcha: don't block the event loop.** A long *synchronous* task - a giant `for` loop, a heavy calculation - freezes everything, since there's only one thread. In the browser the page stops responding (no scrolling, no clicks); on a server, every request stalls. Keep heavy synchronous work off the main thread - let slow things be async, and break up big computations.

> 💡 This phase is the working mental model. For the full picture - the queue, microtasks vs. macrotasks, why one thread can feel concurrent - see [Async/Await and the Event Loop](/guides/async-await-and-the-event-loop).

## Three generations of async syntax

The *idea* (start now, finish later) has stayed the same; the *syntax* for expressing it got dramatically nicer over the years. You'll see all three in real code, so let's meet them in order.

### Callbacks - the original

A **callback** is a function you hand to something slow, with the instruction "call this when you're done." The slow thing holds onto your function and runs it later.

```javascript
setTimeout(() => {
  console.log("2 seconds have passed");
}, 2000);
console.log("This runs first");
```
```console
This runs first
2 seconds have passed
```
*What just happened:* `setTimeout` registered your callback and returned immediately, so the line *after* it ran first. Two seconds later the browser dropped your callback into the queue and the event loop ran it.

**The gotcha.** Callbacks nest. One async step depending on another, depending on another, marches your code rightward into a pyramid everyone calls *callback hell*:

```javascript
getUser(id, (user) => {
  getOrders(user, (orders) => {
    getDetails(orders[0], (details) => {
      // ...three levels deep and still going
    });
  });
});
```
*What just happened:* Each step can only start once the previous one's callback fires, so they nest. It works, but it's hard to read and harder to add error handling to - exactly what Promises were invented to fix.

### Promises - a value that arrives later

A **Promise** is an object that stands in for a result that isn't ready yet - a placeholder with three states: *pending* (still waiting), *fulfilled* (succeeded, here's the value), or *rejected* (failed, here's the error). You attach `.then()` for success and `.catch()` for failure.

```javascript
fetch("https://api.example.com/user/1")
  .then((response) => response.json())
  .then((user) => console.log(user.name))
  .catch((error) => console.log("Request failed:", error));
```
*What just happened:* `fetch` returns a Promise immediately, without waiting for the network. Each `.then` says "when the previous step resolves, run this next." The chain is *flat*, not nested: each step returns a new Promise, so success flows down the `.then`s and any error skips straight to `.catch`. Pyramid gone.

📝 **Terminology.** A Promise *resolves* when it settles successfully (giving you a value) and *rejects* when it fails (giving you an error). "Settled" means done one way or the other.

### async/await - Promises that read like normal code

`async`/`await` is *syntax over Promises*. Mark a function `async`, and inside it you can `await` a Promise, which pauses the function until the Promise settles, then hands you the value as if it were a normal return. The code reads top-to-bottom like ordinary code, but it's still async underneath.

```javascript
async function showUser(id) {
  const response = await fetch(`https://api.example.com/user/${id}`);
  const user = await response.json();
  console.log(user.name);
}
```
*What just happened:* `await fetch(...)` pauses `showUser` until the response arrives, then resumes with it in hand - no `.then` nesting, no callback. The function *looks* synchronous, but `await` is quietly doing the "start now, continue later" dance with the event loop. This is the style you'll write almost all the time.

⚠️ **Gotcha: forgetting `await`.** Drop the `await` and you get the *Promise itself*, not the value inside it - your code marches on before the work is done. This bites everyone:

```javascript
async function showUser(id) {
  const response = fetch(`https://api.example.com/user/${id}`); // no await!
  const user = await response.json(); // boom
}
```
```console
TypeError: response.json is not a function
```
*What just happened:* Without `await`, `response` is a pending Promise, not the resolved `Response` object, and a Promise has no `.json()` method - hence the `TypeError`. When something is `undefined` or "not a function" right after an async call, check for a missing `await` first.

> 💡 Rule of thumb: `await` lives inside an `async` function, on something that returns a Promise (`fetch`, `response.json()`, anything you wrote with `async`).

## The DOM - the page as a live object

Async gets data. The **DOM** is how you put it on screen.

When the browser loads your HTML, it parses it into a tree of objects in memory - one per tag - called the **DOM** (Document Object Model). Your JavaScript doesn't edit the HTML text; it edits *this tree*. Change an object in the tree and the browser instantly re-renders that part of the page.

📝 **Terminology.** *DOM* = Document Object Model. An *element* is one node in that tree (a `<button>`, a `<div>`). `document` is the global object that's the root of the tree and your entry point to it.

Three verbs cover most DOM work: **select** an element, **change** it, and **respond** to events on it.

```javascript
// Select
const button = document.querySelector("#load");
const output = document.querySelector("#output");

// Respond to an event
button.addEventListener("click", () => {
  // Change
  output.textContent = "Loading...";
});
```
*What just happened:* `querySelector` finds elements using CSS-selector syntax (`#load` = the element with `id="load"`). `addEventListener("click", fn)` tells the browser to run that function on every click. Setting `output.textContent` rewrites that element's text, and the user sees it change immediately.

⚠️ **Gotcha: use `textContent`, not `innerHTML`, for plain text.** `innerHTML` parses its input as HTML, so dropping user-supplied text into it can inject markup or scripts (an XSS hole). For plain text, `textContent` is both safer and faster.

## Putting it together: click → fetch → update

The canonical browser flow, and why both halves of this phase matter: the user clicks, you fetch data without freezing the page, the response updates the DOM. Watch the thread stay free the whole time:

```mermaid
sequenceDiagram
  participant User
  participant Button
  participant JS as Your JS
  participant API
  participant DOM
  User->>Button: clicks
  Button->>JS: click event fires
  JS->>API: await fetch(...)
  API-->>JS: JSON response
  JS->>DOM: set textContent
```

```javascript
const button = document.querySelector("#load");
const output = document.querySelector("#output");

button.addEventListener("click", async () => {
  output.textContent = "Loading...";
  try {
    const res = await fetch("https://api.example.com/quote");
    const data = await res.json();
    output.textContent = data.text;
  } catch (err) {
    output.textContent = "Could not load. Try again.";
  }
});
```
*What just happened:* The click handler is an `async` function, so it can `await`. It shows "Loading..." then awaits the fetch, and since that wait is async, the page stays fully responsive (the user can still scroll). When the JSON arrives, it's written into the DOM; if anything fails, `catch` shows a friendly message instead of a silent break. This four-line pattern - set pending state, await, update, catch - is most of what front-end JavaScript *is*.

Async code fails in its own particular ways, which is exactly where the next phase begins.

## Recap

1. JavaScript runs on **one thread**; the **event loop** runs slow work's follow-up later, so the thread never sits and waits.
2. Async syntax evolved: **callbacks** (nest badly) → **Promises** (`.then`/`.catch`, flat) → **async/await** (reads like normal code, still Promises underneath).
3. **`await`** pauses an `async` function until a Promise settles and hands you the value - forgetting it gives you the Promise itself, the #1 async bug.
4. The **DOM** is the page as a live tree of objects: **select** (`querySelector`), **change** (`textContent`), **respond** (`addEventListener`).
5. Everyday browser pattern: **click → `await fetch` → update the DOM**, wrapped in `try/catch`, page never freezing.


---

# Errors & I/O - When Things Go Wrong and Data Comes In

Two things every real program does: touch the outside world (files, networks, user input), and get let down by it - the file isn't there, the network times out, the JSON is malformed. A program that assumes everything works is a program that crashes in front of a user.

This phase is about handling failure on purpose, and about reading the outside world - **I/O** ("input/output"). The error-handling tools are the same everywhere; the I/O tools differ by *runtime* (browser vs. Node), so we'll cover both.

## try / catch / finally - handling the explosion

When JavaScript hits something it can't do - calling a method on `undefined`, parsing broken JSON - it **throws** an error, stopping the current code and looking for someone to catch it. If nobody does, the program (or that operation) crashes. `try/catch` is how you volunteer to catch it.

```javascript
try {
  const data = JSON.parse(text);
  console.log(data.name);
} catch (err) {
  console.log("Bad JSON:", err.message);
} finally {
  console.log("Done either way");
}
```
*What just happened:* The `try` block runs normally; if anything inside it throws, execution jumps straight to `catch` with the error object (`err`), skipping the rest of `try`. `finally` runs no matter what - use it for cleanup you can't skip (closing a connection, hiding a spinner).

📝 **Terminology.** An *error* (or *exception*) is an object describing what went wrong - `err.message` the human text, `err.name` the type (e.g. `TypeError`). To *throw* is to raise one; to *catch* is to handle it.

## throw - raising your own errors

You don't only catch errors the language throws - you `throw` your own when your code hits a situation it can't honor. Throwing an `Error` says "this is broken; whoever called me needs to deal with it."

```javascript runnable
function withdraw(balance, amount) {
  if (amount > balance) {
    throw new Error("Insufficient funds");
  }
  return balance - amount;
}

try {
  withdraw(100, 150);
} catch (err) {
  console.log(err.message);
}
```
```console
Insufficient funds
```
*What just happened:* `throw new Error(...)` immediately stopped `withdraw` and sent the error up to the caller's `try/catch`. Throwing beats returning a magic value like `-1` or `null`, since an error can't be silently ignored - it forces a decision.

⚠️ **Gotcha: throw `Error` objects, not strings.** `throw "oops"` technically works, but a bare string has no stack trace, so you lose the line-number trail. Always `throw new Error("message")`.

## Errors in async code - the part that surprises people

Here's the trap: a rejected Promise is *not* caught by a plain `try/catch` around the call that returns it, because the call returns immediately, before the rejection happens - the error arrives later, on the queue.

**The fix, and the whole reason `async/await` is lovely:** `await`ing a Promise throws its rejection *right there*, so an ordinary `try/catch` around the `await` catches it.

```javascript
async function loadUser(id) {
  try {
    const res = await fetch(`https://api.example.com/user/${id}`);
    if (!res.ok) {
      throw new Error(`Server returned ${res.status}`);
    }
    return await res.json();
  } catch (err) {
    console.log("Could not load user:", err.message);
    return null;
  }
}
```
*What just happened:* Wrapping the `await`s in `try/catch` catches both kinds of failure: a *network* failure (`fetch`'s Promise rejects) and a *bad response* we `throw` ourselves. Note the `res.ok` check - a gotcha of its own, below.

⚠️ **Gotcha: `fetch` does not reject on HTTP errors.** A 404 or 500 is a *successful* round-trip to `fetch`, so its Promise **resolves**, not rejects - it only rejects when the request can't complete at all (no network, DNS failure). Check `res.ok` yourself and throw, as above; skipping this is the single most common `fetch` mistake.

⚠️ **Gotcha: unhandled promise rejections.** A rejected Promise that nothing catches doesn't vanish - it surfaces as a warning, and in modern Node can **crash the process**:

```console
$ node app.js
UnhandledPromiseRejection: This error originated either by throwing
inside of an async function without a catch block...
node:internal/process/promises ... (Use `node --trace-warnings ...`)
```
*What just happened:* An async function rejected and no `try/catch` or `.catch()` was waiting. Every Promise needs a home for its failure - `await` it inside a `try/catch`, or attach a `.catch()`.

## I/O #1 - the network, in the browser: `fetch`

`fetch` is the browser's built-in way to make HTTP requests, returning a Promise of a `Response`. Reading JSON is two steps, and each can fail.

```javascript
async function getQuote() {
  const res = await fetch("https://api.example.com/quote");
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  const data = await res.json(); // parses the body as JSON
  return data;
}
```
*What just happened:* `res.json()` reads the response body and parses it as JSON, and is *also* async (the body may still be streaming in), so it needs its own `await`. If the body isn't valid JSON, `res.json()` rejects, caught by your surrounding `try/catch`.

> 💡 Same `fetch`, deeper dive: status codes, headers, request bodies, and what JSON actually is live in [HTTP and JSON API Basics](/guides/http-and-json-api-basics).

## I/O #2 - files, in Node: `fs`

The browser can't read your hard drive (good - imagine if web pages could). **Node** can, through its built-in **`fs`** ("file system") module. The modern, Promise-based version lives at `node:fs/promises`, so you `await` it just like `fetch`.

```javascript
import { readFile } from "node:fs/promises";

async function loadConfig() {
  try {
    const text = await readFile("config.json", "utf8");
    return JSON.parse(text);
  } catch (err) {
    if (err.code === "ENOENT") {
      console.log("No config file; using defaults.");
      return {};
    }
    throw err; // some other problem - let it bubble up
  }
}
```
*What just happened:* `readFile(path, "utf8")` returns a Promise of the file's contents as a string (`"utf8"` means text, not raw bytes); `JSON.parse` turns that text into an object. The `catch` inspects `err.code`: `ENOENT` ("Error NO ENTry") means the file doesn't exist, handled gracefully - any *other* error is re-thrown, since we don't know how to fix it.

📝 **Terminology.** `ENOENT` is a standard OS error code meaning "no such file or directory," surfaced on `err.code`. Checking `err.code` (rather than the message text) is the reliable way to branch on *which* failure happened.

⚠️ **Gotcha: don't blindly `JSON.parse`.** It throws a `SyntaxError` on malformed input - a stray trailing comma, an empty file, an HTML error page where you expected JSON. Always parse inside a `try/catch` so one bad file doesn't take down your whole program.

## The pattern that ties it together

Every example has the same skeleton, whether the data comes from a network or a disk:

```mermaid
flowchart LR
  Start[await the I/O] --> Ok{Worked?}
  Ok -->|yes| Use[parse + use the data]
  Ok -->|no| Catch[catch: handle or re-throw]
```

*What this shows:* `await` the slow input, branch on success, and always have a `catch` that either recovers or deliberately passes the error up. You don't need to memorize APIs - just this shape, and the discipline to never leave a failure path empty.

## Recap

1. **`try/catch/finally`** handles thrown errors; `finally` always runs (use it for cleanup).
2. **`throw new Error("...")`** raises your own errors - use `Error` objects, not strings, to keep the stack trace.
3. In async code, **wrap `await` in `try/catch`** to catch rejected Promises; an **unhandled rejection** can crash Node.
4. **`fetch` doesn't reject on 404/500** - check `res.ok` and throw yourself; `res.json()` is async and can also fail.
5. I/O is runtime-specific: **`fetch`** for the browser network, **`node:fs/promises`** for Node files - **always guard `JSON.parse`**.


---

# The Ecosystem & Tooling - npm, Runtimes, and the Tools Everyone Uses

You can write perfectly good JavaScript with nothing but a text file and a browser. But the moment you join a real project, you're surrounded by tooling - `package.json`, `node_modules`, a `npm run dev` someone told you to type, configs for things called Prettier and ESLint. None of it is mandatory to *write* JavaScript, but all of it is mandatory to *work with other people's* JavaScript.

Here's the map, in the order you actually meet the pieces - once you know the job each tool does, the config files stop being scary and start being obvious.

## npm - the package manager

**npm** ("Node Package Manager") does two jobs: it downloads code other people wrote (**packages**) into your project, and it runs the **scripts** you've defined. It ships with Node, so if you have Node, you have npm.

📝 **Terminology.** A *package* (or *dependency*) is reusable code published to the npm registry - a date library, a web framework, a testing tool. A *package manager* installs them and tracks which versions you used.

Install something, and npm records it:

```console
$ npm install dayjs
added 1 package in 1s
```
*What just happened:* npm downloaded the `dayjs` package into `node_modules/` and added it to `package.json` under `dependencies` - so anyone who clones your project can run `npm install` and get the exact same packages.

### package.json - the project's identity card

`package.json` describes your project: its name, its dependencies, and its **scripts** - named shortcuts for commands.

```json
{
  "name": "my-app",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "test": "vitest",
    "format": "prettier --write ."
  },
  "dependencies": {
    "dayjs": "^1.11.0"
  }
}
```
*What just happened:* The `scripts` block defines names you run with `npm run <name>` - instead of remembering `vite build`, type `npm run build` (`npm test` and `npm start` are special - drop the `run`). `"type": "module"` tells Node to treat your `.js` files as ES modules, so `import`/`export` work (see [Phase 5](05-modules-and-project-layout.md)).

```console
$ npm run dev

  VITE v7.0.0  ready in 412 ms

  ➜  Local:   http://localhost:5173/
```
*What just happened:* `npm run dev` looked up `"dev"` in your scripts, found `vite`, and ran it - starting a local dev server you can open in a browser. The command you'll type a hundred times a day.

⚠️ **Gotcha: `node_modules` is huge and regenerable - never commit it.** That folder routinely holds tens of thousands of files and hundreds of megabytes, rebuildable anytime from `package.json` with `npm install`. Committing it bloats your repo and causes endless merge conflicts. Put it in `.gitignore`:

```console
$ cat .gitignore
node_modules/
```
*What just happened:* Git now ignores `node_modules/` entirely. What you *do* commit is `package.json` and `package-lock.json` (which pins exact versions) - enough for anyone to reproduce your dependencies.

## Where JavaScript runs - the runtime split

JavaScript needs a **runtime**: a program that actually executes it. Two you'll meet first, and two newer ones worth a sentence.

📝 **Terminology.** A *runtime* is the environment that runs your JavaScript and gives it abilities beyond the language itself (reading files, making network requests, talking to a screen).

- **The browser** (Chrome, Firefox, Safari…) runs JavaScript to make web pages interactive - gives you the DOM and `fetch`, but deliberately *can't* read your filesystem.
- **Node.js** runs JavaScript outside the browser - servers, laptops, build tools. Gives you `fs`, networking, and npm packages, but has *no* DOM (there's no page).

That's the split behind a thousand "why doesn't this work" moments: `document` is undefined in Node because Node has no page; `fs` is undefined in the browser because pages can't touch your disk.

```mermaid
flowchart TD
  JS[Your JavaScript] --> B[Browser runtime]
  JS --> N[Node runtime]
  B --> BD[DOM + fetch, no filesystem]
  N --> ND[fs + servers, no DOM]
```

*What this shows:* Same language, two homes, different superpowers. Knowing *which* runtime your code runs in tells you which APIs are available.

> 💡 **Deno and Bun** are newer alternatives to Node that run JavaScript (and TypeScript directly), aiming for better defaults and speed. Ignore them while learning - Node is still the default you'll meet first - but now you know the names.

## The supporting cast - four tools you'll see everywhere

These aren't part of the language - they're npm packages a project pulls in to make life better. You don't need them to start, but you'll meet them fast.

**Bundler - Vite.** In a real app your code is split across dozens of module files, and the browser would be slow fetching each one. A **bundler** combines and optimizes them into a few tight files, and (in dev) gives instant reloading as you edit. **Vite** is the popular modern choice - you saw it as the `dev` and `build` scripts above.

**Formatter - Prettier.** Argues with nobody about spaces vs. tabs - it just reformats your code to one consistent style automatically, so the whole codebase looks the same.

**Linter - ESLint.** A formatter cares how code *looks*; a **linter** cares whether code is *suspect* - unused variables, `==` where you meant `===`, a forgotten `await`. It flags likely bugs before you run anything.

**Test runner - Vitest / Jest.** Runs your automated tests and reports pass/fail. **Jest** is the long-time standard; **Vitest** is the newer one that pairs naturally with Vite. Either way, `npm test` runs them.

Here's the supporting cast at work in one session:

```console
$ npm run format            # Prettier rewrites files to one style
$ npx eslint .              # ESLint flags suspicious code
/src/app.js
  12:7  warning  'count' is assigned a value but never used  no-unused-vars
  ✖ 1 problem (0 errors, 1 warning)
$ npm test                  # Vitest runs the test suite
 ✓ src/math.test.js (3 tests) 4ms
 Test Files  1 passed (1)
      Tests  3 passed (3)
```
*What just happened:* Three tools, three jobs. Prettier silently tidied formatting. ESLint read the same files and warned that `count` is declared but never used - a likely mistake, caught before runtime. Vitest ran the tests and reported all green. `npx` (ships with npm) runs a package's command without a script entry - handy for one-offs.

📝 **Terminology.** *Linting* is static analysis: inspecting code *without running it* to spot likely bugs and bad patterns. *Formatting* is purely cosmetic - rearranging the same code for consistent looks.

## How the pieces fit

A typical project flows like this: npm installs the dependencies, your editor + Prettier + ESLint keep the source clean as you write, Vite bundles it for the browser, and Vitest proves it works - all triggered through `package.json` scripts.

You don't need to set any of this up by hand on day one - `npm create vite@latest` scaffolds a project with most of it wired up. The value here isn't configuring the tools, it's *recognizing* them, so opening someone's repo and seeing these files, you already know what each one does.

## Recap

1. **npm** installs packages into `node_modules/` and runs **scripts** defined in `package.json` (`npm run dev`, `npm test`).
2. **`node_modules` is huge and regenerable** - gitignore it; commit `package.json` + `package-lock.json` instead.
3. JavaScript runs in two main **runtimes**: the **browser** (DOM, `fetch`, no filesystem) and **Node** (`fs`, servers, no DOM); **Deno/Bun** are newer alternatives.
4. **Vite** bundles many source files into fast browser files; **Prettier** formats, **ESLint** lints (flags likely bugs), **Vitest/Jest** run tests.
5. You don't configure all this by hand - you *recognize* it; scaffolders like `npm create vite@latest` wire it up for you.


---

# Idioms & Common Gotchas - Write It Like a Local, Dodge the Traps

You can now write JavaScript that works. This phase covers writing it the way experienced developers do - and dodging the handful of traps that have confused every JavaScript programmer who ever lived. (Genuinely - the ones in the cheat-card below have wasted millions of collective hours. You're about to skip that.)

Two halves: **idioms** - modern syntax that makes code shorter and clearer, seen in every codebase - then a scannable **gotcha cheat-card**, surprises named before they bite so you recognize them instead of staring at the screen.

## Modern idioms - the way it's written today

### Destructuring - unpack in one line

Pulling values out of an object or array straight into named variables, instead of one assignment per field.

```javascript runnable
const user = { name: "Ada", role: "admin" };

const { name, role } = user;          // object destructuring
const [first, second] = [10, 20];     // array destructuring
console.log(name, role, first);
```
```console
Ada admin 10
```
*What just happened:* `const { name, role } = user` created two variables named after the object's keys in one line - same as `const name = user.name; const role = user.role;` but shorter, and standard. You'll see it constantly in function parameters too: `function greet({ name }) { ... }`.

### Spread & rest - `...` does two jobs

The `...` operator either *spreads* a collection out into pieces, or *gathers* loose pieces into one - depending on where you use it.

```javascript
const a = [1, 2];
const b = [...a, 3, 4];              // spread: copy a's items into a new array
const merged = { ...user, role: "user" }; // spread: copy + override a field

function sum(...nums) {              // rest: gather all arguments into an array
  return nums.reduce((t, n) => t + n, 0);
}
console.log(b, sum(1, 2, 3));
```
```console
[ 1, 2, 3, 4 ] 6
```
*What just happened:* `[...a, 3, 4]` spread `a`'s elements into a brand-new array (copying without mutating). `{ ...user, role: "user" }` copied `user` and overrode one field. In `sum(...nums)`, the same `...` did the opposite - *gathered* every argument into an array. Same symbol, mirror-image jobs.

### Optional chaining `?.` and nullish `??`

`?.` safely reads a property that might not exist; `??` supplies a fallback only when something is `null` or `undefined`.

```javascript runnable
const data = { user: { name: "Ada" } };

console.log(data.user?.name);        // "Ada"
console.log(data.order?.total);      // undefined - no crash
console.log(data.order?.total ?? 0); // 0 - fallback
```
*What just happened:* `data.order?.total` would normally crash (`data.order` is `undefined`, and you can't read `.total` of `undefined`), but `?.` short-circuits to `undefined` instead. Then `?? 0` supplies a default - together they replace whole towers of `if (data && data.order && ...)` checks.

⚠️ **Gotcha: use `??`, not `||`, for defaults - when 0 or "" are valid.** `||` falls back on *any* falsy value, so `count || 10` gives `10` even when `count` is a legitimate `0`. `??` only falls back on `null`/`undefined`, correctly keeping the `0`. Reach for `??` whenever zero or empty-string is a real value.

### Array methods over manual loops

`map`, `filter`, `reduce`, `find`, and friends express *what* you want done to a list, rather than a manual `for` loop spelling out *how*.

```javascript runnable
const nums = [1, 2, 3, 4];
const doubled = nums.map((n) => n * 2);        // transform each
const evens = nums.filter((n) => n % 2 === 0); // keep some
const total = nums.reduce((sum, n) => sum + n, 0); // combine to one
console.log(doubled, evens, total);
```
```console
[ 2, 4, 6, 8 ] [ 2, 4 ] 10
```
*What just happened:* Each method takes a small function and applies it across the array, returning a new array (or value) without an index or counter to manage. The code reads like a sentence - "map each to double" - and you can't fat-finger an off-by-one. The default style for lists.

### Modules over globals

Sharing code through explicit `import`/`export` (Phase 5) rather than dumping everything onto shared global variables.

A global variable is reachable - and *editable* - from anywhere, so any file can quietly break any other. Modules make sharing intentional: a file exports what it means to share, and importers state what they depend on, so when something changes you can trace who's affected. *Prefer the import; avoid the global.*

> 💡 The umbrella idiom: prefer the form that makes intent explicit and prevents silent mistakes. Destructuring names what you took, `??` says exactly when to fall back, modules declare what's shared. Clarity over cleverness.

## The gotcha cheat-card

> **Hit something baffling? Find the symptom here, then read the note below.** These trap *everyone* - recognizing them is the whole battle.

| The trap | What bites you | The fix |
|---|---|---|
| `==` vs `===` | `0 == ""` is `true`; `1 == "1"` is `true` | Always use `===` (and `!==`) |
| `this` binding | `this` is `undefined`/wrong inside a callback | Use arrow functions; they keep the outer `this` |
| Hoisting | A `function` works before its line; `let`/`const` don't | Declare before use; prefer `const` |
| `NaN` | `NaN === NaN` is `false` | Test with `Number.isNaN(x)` |
| Floating point | `0.1 + 0.2 !== 0.3` | Round, or compare with a tolerance |
| Shared references | Copying an object copies the *pointer*, not the data | Copy with `{ ...obj }` / `[...arr]` |
| Truthy/falsy | `if (count)` skips a real `0` | Check explicitly: `if (count > 0)` |

The *why* behind each:

### `==` vs `===`

`==` performs *type coercion*, converting operands to a common type before comparing, producing famous nonsense like `0 == ""` and `false == "0"` both being `true`.

```javascript runnable
console.log(0 == "");      // true  (coerced - surprising)
console.log(0 === "");     // false (no coercion - sane)
```
*What just happened:* `==` quietly converted both sides until they matched; `===` compared type *and* value, so a number and a string are never equal. **Always use `===`** - no surprises, and ESLint will nag you if you slip.

### `this` binding

`this` doesn't mean "the current object" as in some languages - it depends on *how a function is called*, and inside a plain-function callback it often isn't what you expect.

```javascript
const counter = {
  count: 0,
  startBroken() {
    setTimeout(function () { this.count++; }, 100); // `this` is NOT counter
  },
  startFixed() {
    setTimeout(() => { this.count++; }, 100);        // arrow keeps outer `this`
  },
};
```
*What just happened:* In `startBroken`, the plain `function` callback got its own `this` (not `counter`), so `this.count++` fails silently. The **arrow function** in `startFixed` has no own `this` - it borrows the surrounding one, which *is* `counter`. Rule of thumb: arrow functions for callbacks, and the problem mostly disappears.

### Hoisting

`function` declarations are *hoisted* - moved to the top of their scope - so they work before the line they're written on. `let`/`const` are not usable before their declaration.

```javascript runnable
greet();                       // works - function declarations are hoisted
function greet() { console.log("hi"); }

console.log(x);                // ReferenceError - can't use before declaration
const x = 5;
```
*What just happened:* `greet` ran before its definition since function declarations are pulled up. `const x` was *not* usable early - it throws until its line runs. The clean habit: **declare before use** and hoisting stops mattering.

### `NaN`

`NaN` ("Not a Number") is the result of invalid math - the only value in JavaScript not equal to itself.

```javascript runnable
const result = Number("abc"); // NaN
console.log(result === NaN);       // false (!)
console.log(Number.isNaN(result)); // true
```
*What just happened:* `NaN === NaN` is `false` by design, so the obvious check silently fails - use `Number.isNaN(x)` instead. Seeing `NaN` in your output usually means a string-to-number conversion went wrong upstream.

### Floating point

Numbers are stored in binary floating point, which can't represent some decimals exactly, so arithmetic has tiny rounding errors.

```javascript runnable
console.log(0.1 + 0.2);            // 0.30000000000000004
console.log(0.1 + 0.2 === 0.3);    // false
```
*What just happened:* `0.1` and `0.2` have no exact binary form, so their sum is a hair off - not a JavaScript bug, but how floating point works in nearly every language. For money, work in integer cents; for comparisons, round or check `Math.abs(a - b) < 0.0001`.

### Shared references

Objects and arrays are held by *reference*. Assigning one to a new variable doesn't copy the data - both names point at the *same* object, so a change through one is visible through the other.

```javascript runnable
const a = { count: 1 };
const b = a;        // NOT a copy - same object
b.count = 99;
console.log(a.count); // 99 - `a` changed too
```
*What just happened:* `b = a` copied the *reference*, not the contents, so mutating through `b` mutated the one shared object. For an independent copy, spread it: `const b = { ...a }`. Behind countless "why did my other variable change?!" bugs.

### Truthy / falsy

In a condition, non-boolean values are coerced to true/false. The falsy values: `false`, `0`, `""`, `null`, `undefined`, `NaN`. Everything else is truthy.

```javascript runnable
const count = 0;
if (count) console.log("has items");  // never runs - 0 is falsy!
if (count > 0) console.log("has items"); // correct
```
*What just happened:* `if (count)` treated a real `0` as false and skipped the block - a classic bug when `0` is a valid value. When you mean "exists," check explicitly (`count > 0`, `value != null`) rather than trusting truthiness.

## Recap

1. **Idioms:** destructuring unpacks, `...` spreads/gathers, `?.` reads safely, `??` defaults on null/undefined, array methods beat manual loops, modules beat globals.
2. **Always `===`** - `==` coerces types and lies.
3. **`this`** depends on how a function is called; **arrow functions** keep the outer `this`.
4. **`NaN` isn't equal to itself** (`Number.isNaN`); **floats are imprecise** (`0.1 + 0.2`); **objects copy by reference** (spread to clone).
5. **Truthy/falsy** treats `0` and `""` as false - check existence explicitly when valid.


---

# Scope, Closures & Hoisting - How JavaScript Remembers

Phase 9 gave you a cheat-card line about hoisting. This phase is the machinery underneath every variable you've written - once you see it, three confusing behaviors stop being magic: why a loop variable inside `setTimeout` all shows the same number, why `let` and `var` behave differently in blocks, and the big one - **closures**, the feature powering callbacks, event handlers, React hooks, and the module pattern.

The whole phase rests on one question: *when JavaScript sees a variable name, where does it look for the value?* Answer that and everything else falls out of it.

## Scope and the scope chain - where names are looked up

**What it actually is.** **Scope** is the set of variables visible from a given spot in your code. Every function creates a new scope, nested inside the scope it was written in. JavaScript looks for a variable in the current scope first; if it's not there, it checks the scope *outside* that one, then the one outside *that*, all the way to global. That outward chain of lookups is the **scope chain**.

📝 **Lexical scope** - "lexical" means *where you wrote it*. A function's scope is decided by its physical position in the source code, not by where or how it's later called - JavaScript wires up the scope chain by reading your file, before anything runs.

```mermaid
flowchart TD
  A["inner()<br/>sees: secret"] --> B["outer()<br/>sees: name"]
  B --> C["global<br/>sees: appName"]
  A -. "looks up 'appName'" .-> C
```

*One idea:* a name lookup only ever travels *outward*, never inward. `inner` can reach into `outer` and the global scope, but nothing outside `inner` can see `inner`'s variables. Watch the chain resolve a name:

```javascript runnable
const appName = "Manual";          // global scope

function outer() {
  const name = "Ada";              // outer's scope
  function inner() {
    const secret = 42;             // inner's scope
    console.log(secret, name, appName); // found at 3 different levels
  }
  inner();
}
outer();
```
```console
42 Ada Manual
```
*What just happened:* Inside `inner`, JavaScript found `secret` locally. It didn't find `name`, so it stepped out to `outer`'s scope - found it. Same for `appName`, one level further out. Three names, resolved at three links of the chain, all by walking outward.

⚠️ **Gotcha - lookups go out, not in.** Reading `secret` from inside `outer` (but outside `inner`) throws a `ReferenceError`. A scope is a one-way mirror: the inside sees out, the outside can't see in - why ten different functions can each have their own `name` variable without colliding.

## `var` vs `let`/`const` - function scope vs block scope

Here's the first place the rules diverge, and it's the source of a famous bug: the *unit* of scope each keyword respects.

📝 **Block scope** - a "block" is any `{ ... }`: an `if`, a `for`, a bare pair of braces. `let` and `const` are confined to the block they're declared in. **Function scope** - `var` ignores blocks entirely; it's visible throughout the *entire function* it lives in, no matter how deeply nested.

```javascript runnable
function test() {
  if (true) {
    var leaks = "I'm everywhere in test()";
    let trapped = "I'm stuck in this if-block";
    console.log(trapped);          // fine - same block
  }
  console.log(leaks);              // fine - var ignores the block
  console.log(typeof trapped);    // "undefined" - let didn't escape
}
test();
```
```console
I'm stuck in this if-block
I'm everywhere in test()
undefined
```
*What just happened:* `var leaks` was declared inside the `if` block, but `var` ignores blocks - it belongs to the whole function, so it's readable after the `if` closes. `let trapped` obeys the block: outside it, `typeof trapped` reports `"undefined"`. That leak is exactly why `var` causes trouble.

**The classic loop bug.** You loop, schedule some callbacks, and expect them to print `0, 1, 2`. With `var`, they all print `3`:

```javascript runnable
const withVar = [];
for (var i = 0; i < 3; i++) {
  withVar.push(() => i);           // capture i... but which i?
}
console.log(withVar.map((fn) => fn())); // [3, 3, 3]

const withLet = [];
for (let j = 0; j < 3; j++) {
  withLet.push(() => j);
}
console.log(withLet.map((fn) => fn())); // [0, 1, 2]
```
```console
[ 3, 3, 3 ]
[ 0, 1, 2 ]
```
*What just happened:* With `var i`, there's exactly **one** `i` for the whole loop - function-scoped, shared by all three callbacks. By the time they run, the loop has finished and that single `i` is `3`. With `let j`, JavaScript creates a **fresh `j` for each iteration** - three separate variables, each captured with the value it had that round. This is the headline reason to default to `let`/`const` and treat `var` as legacy.

💡 **Key insight.** `let`/`const` being block-scoped isn't just tidier - it makes loops with closures *do what you mean*. The per-iteration binding is the whole fix; reach for `var` essentially never.

## Hoisting, properly - declarations come first

Before running a single line of a scope, JavaScript does a quick first pass: it finds all the declarations and sets them up. *That* is **hoisting** - declarations are processed before execution begins, as if lifted to the top of their scope. But "hoisting" doesn't mean every keyword behaves the same - there are three distinct behaviors.

**Function declarations are fully hoisted** - name and body are available before its line, so you can call it above where it's written.

**`var` is hoisted but left undefined** - the name is set up early (so using it isn't a `ReferenceError`), but its value isn't assigned until the line runs. Read it early and you get `undefined`.

**`let` and `const` are hoisted into the Temporal Dead Zone** - the name is reserved, but touching it before its declaration line throws.

📝 **Temporal Dead Zone (TDZ)** - the stretch from the start of a scope until a `let`/`const` declaration actually runs. The variable exists (the name is reserved) but is off-limits; reading it throws `ReferenceError: Cannot access 'x' before initialization`. It's JavaScript protecting you from using a value that isn't ready yet.

```javascript runnable
console.log(addUp(2, 3));    // 5 - function declaration is fully hoisted
console.log(maybe);          // undefined - var name exists, value doesn't yet
var maybe = "now I have a value";

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

try {
  console.log(strict);       // throws - strict is in the TDZ
  let strict = "too late";
} catch (e) {
  console.log("TDZ error:", e.message);
}
```
```console
5
undefined
TDZ error: Cannot access 'strict' before initialization
```
*What just happened:* `addUp` ran before its definition because function declarations are hoisted whole. `maybe` printed `undefined` since `var` reserved the name but hadn't reached the assignment. `strict` threw, because `let` is hoisted only enough to know it exists. The habit that sidesteps all of this: **declare before you use, and prefer `const`.**

⚠️ **Gotcha - the TDZ is a feature, not a bug.** It's catching a real mistake: using a value the code hasn't computed yet. `var` makes that same mistake silent - `undefined`, with the bug surfacing far away. The TDZ fails loud and early, at the source.

## Closures - a function remembers where it was born

A **closure** is what you get when a function outlives the scope it was created in, and *keeps access to that scope's variables anyway* - it carries its birthplace with it.

📝 **Closure** - a function bundled together with the variables from the scope where it was *defined*. Even long after its outer function has returned, it can still read and update those captured variables - it remembers where it was born, not where it's called.

The counter factory is the canonical example:

```javascript runnable
function makeCounter() {
  let count = 0;                   // lives in makeCounter's scope
  return function () {
    count += 1;                    // reaches outward to count
    return count;
  };
}

const counter = makeCounter();     // makeCounter has now RETURNED
console.log(counter());            // 1
console.log(counter());            // 2
console.log(counter());            // 3

const fresh = makeCounter();       // a brand-new, independent count
console.log(fresh());              // 1
```
```console
1
2
3
1
```
*What just happened:* `makeCounter` ran, created `count`, and returned the inner function - then it was *done*. Normally `count` would vanish with it, but the returned function still references `count`, so JavaScript keeps it alive as long as the function exists. Each call reaches outward to the *same* `count` and bumps it. `fresh` is a separate call, so it gets its own `count` starting at `1` - closures don't share state across separate creations.

💡 **The mental model.** The function doesn't copy `count` - it holds a live link back to the exact scope it was born in. As long as the function is reachable, that scope stays alive: a closure is a function plus a backpack of the variables it grew up with.

**A real use - true data privacy.** Those captured variables aren't reachable from outside the closure - there's no `counter.count` to poke at, since the only way to touch `count` is through the function. That's genuinely private state, something JavaScript otherwise lacks:

```javascript runnable
function once(fn) {
  let called = false;              // private - nobody outside can flip this
  let result;
  return function (...args) {
    if (!called) {
      called = true;
      result = fn(...args);
    }
    return result;
  };
}

const setup = once(() => {
  console.log("running expensive setup...");
  return "ready";
});

console.log(setup());   // runs the body
console.log(setup());   // skips it - returns the cached result
console.log(setup());   // still cached
```
```console
running expensive setup...
ready
ready
ready
```
*What just happened:* `once` returns a wrapper closing over the private flag `called` and the cached `result`. The first call flips `called` and runs the real function; every call after sees `called === true` and returns the stored result without re-running. Nothing outside can reset `called` - it's sealed inside the closure. This pattern guards one-time initialization, single payment submissions, and "load it once" caches everywhere.

## Why it matters - closures are everywhere

You've been using closures since Phase 4 without naming them. Now you can see them:

- **Callbacks and event handlers.** `button.addEventListener("click", () => doThing(config))` closes over `config` - it still works whenever the click fires, minutes later, because the closure kept `config` alive.
- **The module pattern.** Wrapping code in a function and returning only what you want public is the closure-powered way to get private variables - how libraries hid internals before `import`/`export`.
- **React hooks.** `useState` and friends lean entirely on closures - each render's functions close over that render's values. (The loop-variable bug above is the #1 source of "stale closure" confusion in React.)
- **The cost.** A closure keeps its captured variables alive as long as the function lives - usually what you want, but a long-lived closure (an event handler you never remove) quietly keeps everything it captured in memory. Remove handlers you no longer need.

The thread tying the phase together: scope is decided by *where you write code*, hoisting decides *what's ready when execution starts*, and closures are scope *outliving* the function that created it.

## Recap

1. **Scope** is the set of visible variables; a name lookup walks the **scope chain** *outward* - local, then enclosing, then global - and never inward.
2. **Lexical scope** means scope is fixed by *where code is written*, not where it's called - JavaScript wires up the chain by reading the source.
3. **`var` is function-scoped** (it leaks out of blocks); **`let`/`const` are block-scoped**. The per-iteration binding of `let` is what fixes the classic `[3, 3, 3]` loop-in-callback bug.
4. **Hoisting** processes declarations first: functions are fully hoisted, `var` is hoisted-but-`undefined`, and `let`/`const` sit in the **Temporal Dead Zone** until their line runs.
5. A **closure** is a function plus the variables from its birthplace; it keeps those variables alive after the outer function returns, giving you private state and persistent counters.
6. Closures power **callbacks, event handlers, the module pattern, and React hooks** - but a long-lived closure holds its captured variables in memory, so release handlers you no longer need.

## Quick check

Test yourself on where a function looks for its variables:

```quiz
[
  {
    "q": "Why do the `var` callbacks print `[3, 3, 3]` while the `let` callbacks print `[0, 1, 2]`?",
    "choices": [
      "`var` creates one shared, function-scoped `i` for the whole loop; `let` creates a fresh, block-scoped binding each iteration",
      "`let` callbacks run before the loop finishes, but `var` callbacks run after",
      "`var` rounds its values up to the final count, while `let` preserves them",
      "Arrow functions only capture `let` variables, never `var` variables"
    ],
    "answer": 0,
    "explain": "There's exactly one function-scoped `i` with `var`, shared by all three callbacks - by the time they run, it's 3. `let` gives each iteration its own binding, so each callback captures the value it had that round."
  },
  {
    "q": "What is the Temporal Dead Zone?",
    "choices": [
      "The window from the start of a scope until a `let`/`const` line runs, during which accessing the variable throws",
      "A performance penalty for declaring too many variables in one function",
      "The time it takes a closure to release its captured variables from memory",
      "A region of code where `var` declarations are silently ignored"
    ],
    "answer": 0,
    "explain": "`let`/`const` are hoisted enough that the name is reserved, but reading them before their declaration line throws `ReferenceError: Cannot access 'x' before initialization`. That stretch is the TDZ - it fails loud instead of giving you a silent `undefined`."
  },
  {
    "q": "After `const counter = makeCounter()` returns, why can the returned function still increment `count`?",
    "choices": [
      "The returned function is a closure - it holds a live link to the scope where it was defined, keeping `count` alive",
      "`count` was copied into the returned function as a private constant",
      "JavaScript re-runs `makeCounter` on every call to rebuild `count`",
      "`count` was secretly promoted to a global variable when makeCounter returned"
    ],
    "answer": 0,
    "explain": "A closure keeps the variables from its birthplace alive as long as the function exists. The returned function references `count`, so that scope isn't discarded - each call reaches the same live `count` and bumps it. A separate `makeCounter()` call gets its own independent `count`."
  }
]
```


---

# this, Prototypes & the Object Model - How Objects Really Work

Back in [Phase 9](09-idioms-and-gotchas.md) you got a one-line warning about `this`: use arrow functions in callbacks and the weirdness mostly goes away. That's a bandage over a deep idea. This phase peels it off and shows you the machinery underneath - `this`, prototypes, and what a `class` *actually* is.

Hold onto two ideas: **JavaScript objects don't have private blueprints the way classes do in other languages - every object is wired to *another* object it falls back to.** That single linked-list-of-objects idea explains inheritance, methods, and `class` all at once. And `this` isn't part of the object at all - it's decided fresh every time a function runs, based purely on *how* you called it. Get those two and the rest of the language stops surprising you.

## `this` is set by HOW you call, not where you wrote it

This is the single most misunderstood word in JavaScript. In Java or Python, `this`/`self` means "the object this method belongs to," fixed forever. In JavaScript, `this` is a *parameter filled in at call time* - the same function body can see four different `this` values depending on how it's invoked.

📝 **`this`** - an implicit argument every regular function receives. Its value isn't decided when you write the function; it's decided by the **call site**, the exact expression you used to call it.

Four rules, checked in this order:

1. **`new` binding** - called with `new`? `this` is the brand-new object being built.
2. **Explicit binding** - called via `call`, `apply`, or `bind`? `this` is whatever you passed.
3. **Implicit binding** - called as `obj.method()`? `this` is `obj` (the thing left of the dot).
4. **Default binding** - none of the above? `this` is `undefined` in strict mode (modules and classes are always strict), or the global object in sloppy mode.

One function, called four ways, four answers:

```javascript runnable
"use strict";

function whoAmI() {
  return this;
}

const obj = { name: "obj", whoAmI };

console.log(obj.whoAmI().name);        // implicit: left of the dot
console.log(whoAmI());                 // default: nothing left of a dot
console.log(whoAmI.call({ name: "explicit" }).name); // explicit
console.log(new whoAmI());             // new: a fresh object
```
```console
obj
undefined
explicit
{}
```
*What just happened:* The function never changed - only the call site did. `obj.whoAmI()` had `obj` to the left of the dot, so `this` was `obj`. The bare `whoAmI()` had nothing to the left, so in strict mode `this` was `undefined`. `whoAmI.call({...})` forced `this` to the object we handed it, and `new whoAmI()` ignored everything else and made `this` a fresh empty object. Same body, four `this` values, decided entirely by *how* it was called.

⚠️ **Gotcha - pulling a method off its object loses `this`.** `const f = obj.whoAmI; f();` does *not* keep `obj` - the dot is gone at the call site, so you fall through to default binding and `this` is `undefined`. This is exactly what bites you when you pass `obj.method` as a callback: **`this` follows the dot, and the dot has to be there when you call.**

## `call`, `apply`, and `bind` - taking control of `this`

Since `this` is decided by the call site, JavaScript gives you three tools to set it yourself - all answering "run this function, but with `this` set to an object I choose."

- **`fn.call(thisArg, a, b)`** - call `fn` right now, with `this = thisArg`, passing arguments one by one.
- **`fn.apply(thisArg, [a, b])`** - identical, except arguments come as one **array**. (Mnemonic: **a**pply takes an **a**rray.)
- **`fn.bind(thisArg)`** - does *not* call anything. It returns a **new function** with `this` permanently locked to `thisArg`. You call that new function later.

```javascript runnable
function describe(greeting, punct) {
  return `${greeting}, I am ${this.name}${punct}`;
}

const ada = { name: "Ada" };

console.log(describe.call(ada, "Hi", "!"));      // args one by one
console.log(describe.apply(ada, ["Hey", "."]));  // args as an array

const greetAda = describe.bind(ada);             // returns a new function
console.log(greetAda("Hello", "?"));             // this is locked to ada
```
```console
Hi, I am Ada!
Hey, I am Ada.
Hello, I am Ada?
```
*What just happened:* `call` and `apply` both ran `describe` immediately with `this` forced to `ada` - the only difference was argument packaging (loose vs. an array). `bind` was different in kind: it didn't run anything, it manufactured `greetAda`, a new function whose `this` is forever `ada`. We called `greetAda` later and it still remembered. `bind` is how you hand a method to a callback *without* losing its receiver: `setTimeout(obj.method.bind(obj), 100)`.

💡 **Key point.** `call`/`apply` mean "invoke now with this `this`"; `bind` means "make me a pre-wired copy for later." Reach for `bind` when something else does the calling (a timer, an event handler) and `this` needs to survive the trip.

## Arrow functions have no `this` of their own

Arrow functions break all four rules above on purpose - an arrow **doesn't get its own `this`**. Writing `this` inside an arrow doesn't fill it in at call time; it's read from the surrounding scope where the arrow was *defined*, exactly like any other variable. This is **lexical `this`** - a feature when the arrow is a callback, a bug when it's a method.

```javascript runnable
"use strict";

const timer = {
  seconds: 0,
  startBroken() {
    // regular function callback: gets its OWN this (not `timer`)
    [1, 2].forEach(function () { this.seconds++; });
  },
  startFixed() {
    // arrow callback: borrows startFixed's this, which IS `timer`
    [1, 2].forEach(() => { this.seconds++; });
  },
};

try { timer.startBroken(); } catch (e) { console.log("broken throws:", e.constructor.name); }
timer.startFixed();
console.log("seconds:", timer.seconds);
```
```console
broken throws: TypeError
seconds: 2
```
*What just happened:* In `startBroken`, the plain `function` callback followed the default rule - in strict mode its `this` was `undefined`, so `this.seconds++` threw a `TypeError`. In `startFixed`, the arrow had no `this` of its own, so it reached outward to `startFixed`'s `this`, which implicit binding had set to `timer`. The arrow "captured" the right receiver automatically - **this is why arrows fixed the callback problem from Phase 9.**

⚠️ **Gotcha - never use an arrow as an object method.** `const obj = { name: "x", hi: () => this.name }` is broken: the arrow captures `this` from wherever `obj` was defined (the module top level, where `this` is `undefined`), *not* from `obj`. There's no dot magic for arrows. Methods that need `this` to mean "my object" must be regular functions (or the `method() {}` shorthand); save arrows for callbacks.

## The prototype chain - how property lookup really works

Now the other half. Every JavaScript object has a hidden internal link to *another* object, its **prototype**. When you read a property the object doesn't have, JavaScript doesn't give up - it follows that link and looks on the prototype, then *that* object's link, and so on, until it finds the property or hits `null`. That chain of fallback objects is the **prototype chain**.

📝 **Prototype** - the object a given object falls back to for properties it doesn't have itself. The hidden link is reachable via `Object.getPrototypeOf(obj)` or the legacy `obj.__proto__`.

This is the entire inheritance model of the language: no classes underneath, just objects pointing at objects.

```mermaid
flowchart LR
  A["dog<br/>(instance)"] --> B["Dog.prototype<br/>speak()"]
  B --> C["Object.prototype<br/>toString()"]
  C --> D["null"]
```

The lookup walks left to right. Watch a method get found two links up the chain, even though the instance itself doesn't have it:

```javascript runnable
function Dog(name) {
  this.name = name;                      // own property, on the instance
}
Dog.prototype.speak = function () {      // shared method, on the prototype
  return `${this.name} says woof`;
};

const d = new Dog("Rex");

console.log(d.speak());                                   // found on Dog.prototype
console.log(d.hasOwnProperty("name"));                   // true - own property
console.log(d.hasOwnProperty("speak"));                  // false - it's inherited
console.log(Object.getPrototypeOf(d) === Dog.prototype); // true - the hidden link
```
```console
Rex says woof
true
false
true
```
*What just happened:* `d` itself only had one property: `name`, set by `this.name = name`. Calling `d.speak()`, JavaScript looked on `d`, didn't find `speak`, followed the hidden link to `Dog.prototype`, found it there, and ran it with `this = d` (implicit binding, because of the dot). That's why `hasOwnProperty("speak")` is `false`: the method lives on the prototype, *shared* by every `Dog`, not copied onto each instance.

💡 **Key point.** Own properties (set with `this.x =`) live on the instance; methods live on the prototype, shared by all instances. Property *writes* always go on the instance, but property *reads* walk the chain - this is what makes prototypes memory-efficient: a thousand dogs share one `speak`.

## Classes are sugar over prototypes

In Phase 7 you wrote `class`. Here's the reveal: **`class` doesn't add a new object model** - it's a cleaner, less error-prone *spelling* of the exact prototype wiring you just saw. A class method lands on `.prototype`; `extends` links one prototype to another; `super` walks up the chain. Same machinery, nicer syntax.

```javascript runnable
class Animal {
  constructor(name) { this.name = name; }
  speak() { return `${this.name} makes a sound`; }
}

class Dog extends Animal {
  speak() { return `${super.speak()} (woof)`; } // super = up the chain
}

const d = new Dog("Rex");
console.log(d.speak());

// Proof it's the same prototype mechanism from the previous section:
console.log(d.hasOwnProperty("speak"));                          // false - on prototype
console.log(Object.getPrototypeOf(d) === Dog.prototype);         // true
console.log(typeof Dog.prototype.speak);                         // "function"
console.log(Object.getPrototypeOf(Dog.prototype) === Animal.prototype); // extends = linked prototypes
```
```console
Rex makes a sound (woof)
false
true
function
true
```
*What just happened:* `Dog`'s `speak` lives on `Dog.prototype` (not the instance - `hasOwnProperty` is `false`), identical to the hand-rolled version above. `extends` set `Dog.prototype`'s own prototype to `Animal.prototype`, so `super.speak()` reached one link up and ran `Animal`'s method. The class syntax wrote all the `Dog.prototype.x = ...` plumbing for you, plus guardrails (no calling a class without `new`, non-enumerable methods) - but strip the syntax away and it's the same chain of objects.

⚠️ **Gotcha - `class` is not a new kind of thing.** Don't think "objects vs. classes" like in Java. A JavaScript `class` is a function whose `.prototype` is pre-loaded with your methods. `instanceof` really means "is this prototype anywhere in your chain?", and `Object.getPrototypeOf` always shows you the truth, no matter how the object was created.

## Recap

1. **`this` is decided by the call site, not the definition.** Four rules, in priority order: `new` → explicit (`call`/`apply`/`bind`) → implicit (`obj.method()`) → default (`undefined` in strict mode).
2. **The dot must be present at the call** for implicit binding. Pulling a method off its object (or passing it as a callback) strips the receiver and falls through to default.
3. **`call` and `apply` invoke now** with a `this` you choose (loose args vs. an array); **`bind` returns a new function** with `this` locked in for later.
4. **Arrow functions have no own `this`** - they capture it lexically from where they're defined. Perfect for callbacks, wrong for object methods.
5. **Every object links to a prototype.** Property reads walk the **prototype chain** until found or `null`; writes go on the instance. Shared methods live on the prototype.
6. **Classes are sugar over prototypes.** Methods go on `.prototype`, `extends` links prototypes, `super` walks up. Same object model, cleaner spelling.

Next: **iterators, generators, and Symbols** - the protocols that let your own objects plug into `for...of`, spread, and destructuring.

## Quick check

Test yourself on call-site `this` and the prototype chain:

```quiz
[
  {
    "q": "Given `const obj = { n: 1, get() { return this.n; } }`, what does `const f = obj.get; f();` return in strict mode?",
    "choices": [
      "It throws a TypeError - `this` is undefined because the dot is gone at the call site",
      "1 - `this` is permanently bound to obj when the method is defined",
      "undefined - but no error, because `this` quietly becomes the global object",
      "1 - pulling a method off an object keeps its receiver"
    ],
    "answer": 0,
    "explain": "`this` follows the dot, and the dot must be present at the call. `f()` has nothing to the left, so default binding makes `this` undefined in strict mode, and `this.n` throws a TypeError. This is exactly why callbacks lose their receiver."
  },
  {
    "q": "What's the difference between `fn.apply(obj, [1, 2])` and `fn.bind(obj)`?",
    "choices": [
      "`apply` calls fn immediately with `this` = obj; `bind` returns a new function with `this` locked to obj for later",
      "They're identical; `bind` is just an older alias for `apply`",
      "`apply` takes arguments one by one; `bind` takes them as an array",
      "`bind` calls fn immediately; `apply` returns a new function"
    ],
    "answer": 0,
    "explain": "`apply` (like `call`) invokes the function right now with a chosen `this` - it just takes its arguments as an array. `bind` doesn't invoke anything; it manufactures a new function whose `this` is permanently fixed, to be called later."
  },
  {
    "q": "For `const d = new Dog('Rex')` where `speak` is defined as a class method, what does `d.hasOwnProperty('speak')` return, and why?",
    "choices": [
      "false - class methods live on Dog.prototype, so `speak` is inherited via the prototype chain, not an own property of d",
      "true - `new` copies every class method onto the instance",
      "true - class methods are always own properties, unlike old prototype methods",
      "It throws, because hasOwnProperty doesn't work on class instances"
    ],
    "answer": 0,
    "explain": "Classes are sugar over prototypes: `speak` lives on `Dog.prototype`, shared by all instances. `d` itself only owns properties set in the constructor (like `name`). Reading `d.speak` walks the chain to the prototype, so `hasOwnProperty('speak')` is false."
  }
]
```


---

# Iterators, Generators & Symbols - Producing Values on Demand

You've written `for (const x of arr)` since early in this guide. But what does that loop actually *do*, and why does it work on arrays, strings, `Map`s, and `Set`s - yet throw a fit on a plain object? This phase pulls back the curtain.

One idea runs underneath it: **producing values one at a time, on demand**, instead of building the whole collection up front. See how `for...of` asks for the next value, and two tools fall into your lap: making *your own* objects loopable, and writing functions that pause mid-execution, hand back a value, and resume later. That last trick - generators - lets you describe an *infinite* sequence without your machine catching fire.

## The iterable protocol - what `for...of` really does

**What it actually is.** An **iterable** is anything you can loop over with `for...of`. An **iterator** is the thing that walks through it. The iterable is the book; the iterator is the bookmark.

📝 **Iterable** - an object with a `[Symbol.iterator]()` method that returns an iterator. **Iterator** - an object with a `.next()` method that returns `{ value, done }` each time you call it. `for...of` asks the iterable for a fresh iterator, then pulls items until `done` is `true`.

When you write `for (const x of things)`, the engine does three things under the hood:

1. Calls `things[Symbol.iterator]()` to get an iterator.
2. Calls `.next()` on that iterator over and over; each call returns `{ value, done }`.
3. Stops the moment a `.next()` comes back with `done: true`.

```mermaid
flowchart LR
  A[for...of things] --> B["things[Symbol.iterator]()"]
  B --> C["iterator.next()"]
  C -->|"{value, done:false}"| D[run loop body]
  D --> C
  C -->|"{done:true}"| E[loop ends]
```

Drive that machinery by hand to watch it move:

```javascript runnable
const things = ["a", "b"];
const it = things[Symbol.iterator]();   // get an iterator (the bookmark)

console.log(it.next());                 // pull the first item
console.log(it.next());                 // pull the second
console.log(it.next());                 // nothing left
```
```console
{ value: 'a', done: false }
{ value: 'b', done: false }
{ value: undefined, done: true }
```
*What just happened:* `things[Symbol.iterator]()` made an iterator that remembers its position. Each `.next()` advanced it by one and returned a `{ value, done }` record. After the last real item, `.next()` returned `done: true` - the signal `for...of` watches for to stop. A `for...of` loop is this, with the `.next()` calls and `done` check handled for you.

💡 **Why this saves you later.** Once `for...of` means "call `.next()` until `done`," a pile of JavaScript stops being mysterious: why `{a: 1}` can't be looped with `for...of` (no `[Symbol.iterator]`), why arrays, strings, `Map`, and `Set` all *can*, and - coming up - how generators plug straight into every `for...of` you'll write.

## Symbols, briefly - collision-proof keys

You just saw `Symbol.iterator` show up as an object key.

**What it actually is.** A `Symbol` is a primitive value whose entire purpose is to be **unique**. Every call to `Symbol()` produces a brand-new value equal to nothing but itself - even two made from the same description are different.

📝 **Symbol** - a unique, unforgeable primitive, often used as an object key when you need a name that can't clash with any string key. `Symbol("x") !== Symbol("x")`.

```javascript runnable
const a = Symbol("id");
const b = Symbol("id");
console.log(a === b);                    // false - every Symbol is unique

const user = { name: "Ada" };
user[a] = 42;                            // use a Symbol as a key
console.log(user[a]);                    // 42
console.log(Object.keys(user));          // ['name'] - Symbol key is hidden
```
```console
false
42
[ 'name' ]
```
*What just happened:* `a` and `b` describe the same thing (`"id"`) but are distinct values, so `a === b` is `false`. As a key, `a` stored `42` on `user` without touching any string property - `Object.keys` didn't even list it. A Symbol key lives in its own namespace and can never overwrite a normal `name`/`role`/`length` property.

This is why the iterable protocol uses `Symbol.iterator` instead of a string like `"iterator"`: a plain-string hook would risk colliding with any object that happened to have a property named `iterator`. `Symbol.iterator` is a single well-known Symbol shared across the runtime, immune to that collision.

## Make your own object iterable

Because `for...of` only needs `[Symbol.iterator]`, you can teach *any* object to be loopable with that one method. Here's a `range` object yielding numbers from `start` up to (not including) `end`:

```javascript runnable
function range(start, end) {
  return {
    [Symbol.iterator]() {            // the hook for...of looks for
      let current = start;
      return {
        next() {                     // the iterator: one .next() at a time
          if (current < end) {
            return { value: current++, done: false };
          }
          return { value: undefined, done: true };
        },
      };
    },
  };
}

for (const n of range(1, 4)) {
  console.log(n);
}
console.log([...range(1, 4)]);       // spread works too - it uses the protocol
```
```console
1
2
3
[ 1, 2, 3 ]
```
*What just happened:* `range(1, 4)` returned a plain object with a `[Symbol.iterator]` method. Calling that method got back an iterator holding its own `current` counter. Each `.next()` returned the next number and bumped `current`; once `current` hit `end`, it returned `done: true` and the loop stopped. The spread `[...range(1, 4)]` worked for free - spread, destructuring, and `for...of` all speak the *same* protocol, so implementing it once unlocks all of them.

⚠️ **Gotcha - keep the counter inside the method, not on the object.** `current` lives inside `[Symbol.iterator]()`, so each loop gets a fresh `current` starting at `start`. Store it as a property on the returned object instead, and the *second* loop over the same `range` would start where the first left off - empty. State belongs in the iterator, not the iterable, so the same iterable can be looped twice.

## Generators - a function that pauses and resumes

Writing `[Symbol.iterator]` with a hand-rolled `next()` and a manual `{ value, done }` is a lot of ceremony. JavaScript has a far easier way: the **generator**.

**What it actually is.** A **generator function** is written `function*` and uses `yield` instead of `return`. Calling it doesn't run the body - it hands back an iterator. Each pull runs the function until the next `yield`, hands that value out, then **freezes right there**, remembering all its local variables until thawed by the next pull.

📝 **`yield`** - like `return`, but instead of ending the function it pauses it and produces one value. The function picks up where it left off on the next pull. A `function*` containing `yield` is a generator.

**Why this exists.** `return` ends a function and discards everything it knew; `yield` produces a value *without* ending, so a single function can emit a whole stream over time. That's the "one item at a time, remember where you were" behavior the iterator protocol wants - and a generator's returned object already has `.next()` *and* `[Symbol.iterator]`, so it drops straight into `for...of`.

```javascript runnable
function* countToThree() {
  console.log("  -> starting");
  yield 1;
  console.log("  -> resumed after 1");
  yield 2;
  console.log("  -> resumed after 2");
  yield 3;
}

for (const n of countToThree()) {
  console.log("got", n);
}
```
```console
  -> starting
got 1
  -> resumed after 1
got 2
  -> resumed after 2
got 3
```
*What just happened:* Calling `countToThree()` ran *none* of the body - it returned a generator object. `for...of` pulled the first value, which ran the function up to `yield 1` and froze. Pulling again thawed it right after that `yield`, ran to `yield 2`, and froze again. The interleaved logs prove the function is genuinely pausing and resuming, not running all at once - and it's far shorter than the hand-rolled `range`, because `yield` *is* the protocol, written for you.

`range` as a generator, in a fraction of the code:

```javascript runnable
function* range(start, end) {
  for (let n = start; n < end; n++) {
    yield n;
  }
}

console.log([...range(1, 5)]);
for (const n of range(10, 13)) console.log(n);
```
```console
[ 1, 2, 3, 4 ]
10
11
12
```
*What just happened:* The `function*` does everything the verbose version did - `for...of` and spread both work - with no `{ value, done }` bookkeeping and no nested object. `yield` inside the loop hands out each number and pauses; the engine builds the `{ value, done }` records and `[Symbol.iterator]` automatically.

⚠️ **Gotcha - a generator is single-use.** A generator object is an iterator, and an iterator gets *consumed*: once walked to the end, it's empty forever. Loop the *same* generator object a second time and you get nothing.

```javascript runnable
function* squares() {
  for (let n = 0; n < 3; n++) yield n * n;
}

const gen = squares();
console.log("first pass: ", [...gen]);   // drains it
console.log("second pass:", [...gen]);   // already empty
```
```console
first pass:  [ 0, 1, 4 ]
second pass: []
```
*What just happened:* The first spread pulled every value until `done: true`, exhausting the generator. The second spread started where the first left off - at the end - so it got an empty array. To iterate twice, call the generator *function* again for a fresh one (`squares()`), or, if the data is small, materialize it once into an array and reuse that. (`range(1, 5)` works fresh each time precisely because each call is a brand-new generator.)

## Lazy sequences - produce the infinite without storing it

Generators can do something an array fundamentally *can't*: describe a sequence that never ends. An array can't store endless items, but a generator can *describe* one and produce it on demand - you only pay for the values you actually pull.

```javascript runnable
function* naturals() {
  let n = 0;
  while (true) {            // never ends on its own
    yield n++;
  }
}

const gen = naturals();
const firstFive = [];
for (let i = 0; i < 5; i++) {
  firstFive.push(gen.next().value);   // pull exactly five, then stop asking
}
console.log(firstFive);
```
```console
[ 0, 1, 2, 3, 4 ]
```
*What just happened:* `naturals()` would yield numbers forever - the `while (true)` never finishes. Nothing is computed until you ask, so we pulled exactly five values and walked away; the generator is now paused mid-`while`, holding `n`, ready to continue if we come back. ⚠️ Never write a bare `for (const x of naturals())` with no break - it runs until you kill the tab. Always cap how many values you pull.

A practical version: a unique-ID generator. No global counter variable, no risk of two parts of your code resetting it - the state lives safely inside the generator:

```javascript runnable
function* idGenerator(prefix = "id") {
  let n = 1;
  while (true) {
    yield `${prefix}-${n++}`;
  }
}

const nextId = idGenerator("user");
console.log(nextId.next().value);   // user-1
console.log(nextId.next().value);   // user-2
console.log(nextId.next().value);   // user-3
```
```console
user-1
user-2
user-3
```
*What just happened:* `idGenerator` holds `n` privately and bumps it on every `.next()` - an endless, self-incrementing supply with zero shared mutable state floating around your module. A common real-world reason to reach for a generator even when "infinite" sounds exotic.

💡 **Generator vs array - when to reach for which.** Use an **array** when you need the whole collection in hand: to index it, loop it more than once, get its `.length`, or pass it around. Reach for a **generator** when values are produced one pass at a time - especially if the sequence is huge, expensive to compute, infinite, or you'll bail out early. Rule of thumb: if it feeds a single `for...of` or you only want the first few items, a generator keeps memory flat no matter how big the source is.

## Recap

1. `for...of` calls `obj[Symbol.iterator]()` to get an **iterator**, then calls `.next()` - which returns `{ value, done }` - until `done` is `true`. That's the whole **iterable protocol**.
2. A **Symbol** is a unique, collision-proof primitive; the protocol hangs off the well-known `Symbol.iterator` so it can never clash with your own string keys.
3. You can make **any object iterable** by giving it a `[Symbol.iterator]()` method that returns an object with a `.next()` - and that one method also unlocks spread and destructuring.
4. A **generator** (`function*` + `yield`) is the easy way: it pauses and resumes, producing a stream while remembering its place, and plugs straight into `for...of`.
5. ⚠️ A generator object is **single-use** - once exhausted it's empty. Call the generator function again for a fresh one.
6. Generators enable **lazy and infinite sequences**: describe an endless stream, pay only for the values you pull, and keep memory flat.

## Quick check

Test yourself on the ideas that make `for...of` and generators tick:

```quiz
[
  {
    "q": "What does `for...of someThing` call first to start looping?",
    "choices": [
      "someThing[Symbol.iterator]() to obtain an iterator",
      "someThing.next() directly on the object itself",
      "someThing.forEach() with an internal callback",
      "Object.keys(someThing) to list its properties"
    ],
    "answer": 0,
    "explain": "for...of looks up the well-known Symbol.iterator method, calls it to get an iterator, then repeatedly calls that iterator's .next() until it returns { done: true }. A plain object lacks Symbol.iterator, which is why it can't be used with for...of."
  },
  {
    "q": "Why does the iterable protocol use `Symbol.iterator` instead of a plain string key like `\"iterator\"`?",
    "choices": [
      "A Symbol is a unique key, so the language's hook can never collide with your own string properties",
      "Symbols are faster to look up than strings in every engine",
      "String keys are not allowed as method names in JavaScript",
      "It only works that way for historical reasons with no real benefit"
    ],
    "answer": 0,
    "explain": "Symbol.iterator is a single well-known, unique Symbol. Because it isn't a string, no object property you create can accidentally clash with the protocol's hook."
  },
  {
    "q": "You write `const g = squares();` then spread `[...g]` twice in a row. What does the second spread produce?",
    "choices": [
      "An empty array [] - the generator was exhausted by the first spread",
      "The same array as the first spread - generators restart automatically",
      "An error, because you can't spread a generator twice",
      "Half the values, because the generator remembers its midpoint"
    ],
    "answer": 0,
    "explain": "A generator object is single-use. The first spread drains it to done:true, leaving it empty forever. To iterate again, call squares() for a fresh generator, or materialize the values into an array once."
  }
]
```


---

# The Event Loop, Deep - Tasks, Microtasks & Why Order Surprises You

Back in [Phase 6](06-async-and-the-dom.md) you learned to *use* promises and `async`/`await`. That's enough to ship, but sooner or later you'll write code that prints things in an order that makes no sense. (The engine isn't broken - it's doing exactly what it's told, you just haven't seen the rulebook yet.)

This phase is that rulebook: the single thread, the call stack, the two queues that feed it, and the one rule that explains every surprising print order you'll hit. Once this model is in your head, async JavaScript stops being magic and becomes *predictable*.

## JavaScript is single-threaded - "async" means deferred, not parallel

The mental model to carry through this phase: **JavaScript runs your code on exactly one thread, with exactly one call stack.** There is no second thread quietly running your `setTimeout` callback "in the background." When something is "async," it doesn't run *alongside* your code - it runs *later*, after the current work is completely finished.

📝 **Single-threaded** - only one piece of JavaScript can execute at any instant. The engine runs the current code *to completion*, never pausing it halfway to slip in something else, then picks up the next chunk of work.

"Async" lets you *register work to be done later* without blocking. Calling `setTimeout` or `.then()` doesn't run that callback - it hands it to the runtime with "run this when you get a chance," and the thread keeps going. When the stack is empty, the **event loop** hands those waiting callbacks back one at a time.

💡 **In one sentence:** the engine runs the current code to completion, then a loop hands it more work, forever. "Concurrency" in JavaScript is this hand-off dance, not two things truly running at once.

## The call stack - synchronous code runs to empty first

The **call stack** is the engine's to-do list for *right now*. Every function call pushes a frame on; every `return` pops one off. While anything is on the stack, the engine is busy and nothing async can interrupt it - async callbacks only get their turn when the stack is **empty**.

```javascript runnable
function inner() {
  console.log("inner");
}
function outer() {
  console.log("outer start");
  inner();
  console.log("outer end");
}

console.log("script start");
outer();
console.log("script end");
```
```console
script start
outer start
inner
outer end
script end
```
*What just happened:* Every line is synchronous, so it ran top to bottom: `outer()` pushed a frame, called `inner()` (another frame), `inner` popped, then `outer` popped. Nothing deferred, nothing queued. The interesting part starts when we add work that *can't* run now.

⚠️ **Gotcha - synchronous code blocks everything, including the UI.** With one thread, a long-running synchronous loop freezes the whole page: no clicks, no rendering, no timers fire, until your code returns and the stack goes empty. "Don't block the thread" is the cardinal rule of browser JavaScript.

## Two queues: macrotasks vs microtasks

When the stack is empty, the event loop pulls more work from two separate queues - their difference is the secret behind every confusing ordering puzzle.

📝 **Macrotask queue** (also called the *task* queue) - callbacks from `setTimeout`, `setInterval`, I/O, and DOM events (a click handler, etc.). The loop takes **one** macrotask, runs it to completion, then checks the microtasks.

📝 **Microtask queue** - promise reactions (`.then`, `.catch`, `.finally`), the continuation after an `await`, and anything passed to `queueMicrotask`. These run as soon as possible after the current work, before the page does anything else.

The single rule that governs all of it:

> **After each macrotask (and after the initial script finishes), the engine drains the *entire* microtask queue before taking the next macrotask.** Not one microtask - *all* of them, including any new microtasks those add along the way.

The cycle: run a task → empty the whole microtask queue → (let the browser render if needed) → take the next task → repeat.

```mermaid
flowchart TD
  A[Run current task<br/>to stack empty] --> B{Microtasks<br/>waiting?}
  B -->|yes| C[Run next microtask]
  C --> B
  B -->|no| D[Browser may render]
  D --> E[Take ONE macrotask]
  E --> A
```

*One idea:* a macrotask is one "turn," and every turn ends by flushing *all* pending microtasks. That's why a promise callback queued this turn beats a `setTimeout` queued *earlier* - the microtask drains before the timer's turn arrives.

## The classic ordering puzzle

The example that confuses everyone first. Predict the output before you run it.

```javascript runnable
console.log("1: sync start");

setTimeout(() => {
  console.log("4: setTimeout (macrotask)");
}, 0);

Promise.resolve().then(() => {
  console.log("3: promise (microtask)");
});

console.log("2: sync end");
```
```console
1: sync start
2: sync end
3: promise (microtask)
4: setTimeout (macrotask)
```
*What just happened:* The rule in three beats. **First**, all synchronous code runs to completion, parking `setTimeout`'s callback in the *macrotask* queue and the `.then` callback in the *microtask* queue - neither runs yet. **Second**, the script (itself the first macrotask) finishes, so the engine drains the entire microtask queue: the promise callback fires. **Third**, only now does the loop take a macrotask: the timeout fires. Even though `setTimeout` was written *above* the promise with a `0` delay, the promise wins - microtasks always drain before the next macrotask.

⚠️ **Gotcha - `setTimeout(fn, 0)` does not mean "run now."** It means "run after the current synchronous code *and* after every queued microtask." The `0` is a *minimum* delay, not a promise of immediacy. For something to happen truly next, use a microtask (`queueMicrotask(fn)` or `Promise.resolve().then(fn)`) - it jumps ahead of any timer.

Microtasks also chain ahead of macrotasks - watch what happens when a microtask queues *another* microtask:

```javascript runnable
setTimeout(() => console.log("D: timeout"), 0);

Promise.resolve()
  .then(() => console.log("B: promise 1"))
  .then(() => console.log("C: promise 2"));

console.log("A: sync");
```
```console
A: sync
B: promise 1
C: promise 2
D: timeout
```
*What just happened:* The sync log runs first. The script ends, so the engine drains microtasks: the first `.then` runs, and its return value schedules the second `.then` as a *new* microtask - which runs in the same drain pass, since the rule says empty the queue *completely*. Only once it's truly empty does the loop reach for the timeout. The whole promise chain finishes before a single `setTimeout` gets a look-in.

Worth poking at by hand - swap a `setTimeout` for a `queueMicrotask` and watch the order shift:

```playground-eventloop
```

## Why it matters

**`await` resumes as a microtask.** When an `async` function hits `await`, it pauses and the rest of the function is scheduled to continue *as a microtask* once the awaited value settles - why code after an `await` runs before a pending `setTimeout`, and why two `async` functions can interleave in surprising ways.

```javascript runnable
async function go() {
  console.log("2: before await");
  await null;                       // pauses; the rest becomes a microtask
  console.log("4: after await");
}

console.log("1: sync start");
go();
setTimeout(() => console.log("5: timeout"), 0);
console.log("3: sync end");
```
```console
1: sync start
2: before await
3: sync end
4: after await
5: timeout
```
*What just happened:* Calling `go()` runs synchronously up to the `await`, printing immediately. The `await` suspends the function and queues its continuation as a microtask. Control returns to the top level, which parks the `setTimeout` macrotask and prints its own line. The script ends, microtasks drain and the continuation resumes, and only then does the macrotask run - the line *after* `await` behaves precisely like a `.then` callback, because under the hood, it is one.

⚠️ **Gotcha - a flood of microtasks can starve rendering and timers.** Since the engine drains the *entire* microtask queue before the next macrotask (and before the browser repaints), a microtask that keeps queuing more microtasks locks the loop in an endless drain - page frozen, `setTimeout` never firing, frame never painting. The classic footgun is a recursive `queueMicrotask` or a promise chain that never terminates. To yield back to the browser, use a macrotask like `setTimeout(fn, 0)` instead - it explicitly waits for the *next* turn.

💡 **Key point.** Microtasks mean "finish this before anything else happens" (settling promises, `.then` chains). Macrotasks mean "let the world catch up first" (rendering, user input, the next tick) - choosing the right one is choosing *when relative to the browser* your code runs.

## Recap

1. **JavaScript is single-threaded.** One call stack runs the current code to completion; "async" means a callback is *deferred* to run later, not run in parallel.
2. **The event loop** feeds the empty stack from two queues: **macrotasks** (`setTimeout`, I/O, DOM events) and **microtasks** (promise reactions, `await` continuations, `queueMicrotask`).
3. **The rule:** after each macrotask (and after the initial script), the engine drains the **entire** microtask queue - including microtasks added during the drain - before taking the next macrotask.
4. That's why `Promise.resolve().then(...)` always runs **before** a `setTimeout(..., 0)` queued in the same turn - `setTimeout(fn, 0)` means "after the current work and all microtasks," not "now."
5. **`await` resumes as a microtask** - the code after `await` is effectively a `.then` callback, so it runs ahead of pending timers.
6. ⚠️ A runaway flood of microtasks can **starve rendering and timers**; use a macrotask (`setTimeout(fn, 0)`) to yield back to the browser.

You can now predict the order of any mix of sync code, promises, and timers - the single most reliably confusing thing in JavaScript. Next: functional JavaScript.

## Quick check

```quiz
[
  {
    "q": "A script logs `A`, schedules `setTimeout(() => log('B'), 0)`, then `Promise.resolve().then(() => log('C'))`, then logs `D`. What order prints?",
    "choices": [
      "A, D, C, B",
      "A, B, C, D",
      "A, D, B, C",
      "A, C, D, B"
    ],
    "answer": 0,
    "explain": "Synchronous code runs first: A then D. The script (a macrotask) ends, so the engine drains all microtasks before the next macrotask - the promise callback C runs. Only then does the setTimeout macrotask B run. So: A, D, C, B."
  },
  {
    "q": "What does `setTimeout(fn, 0)` actually guarantee about when `fn` runs?",
    "choices": [
      "It runs after the current synchronous code AND after every queued microtask - as the next macrotask, not immediately",
      "It runs immediately, before any other code",
      "It runs before any pending promise callbacks",
      "It runs exactly 0 milliseconds later, interrupting whatever is on the stack"
    ],
    "answer": 0,
    "explain": "The 0 is a minimum delay, not 'now.' fn is a macrotask, so it waits for the current code to finish and the entire microtask queue to drain. A microtask (queueMicrotask / Promise.then) always jumps ahead of it."
  },
  {
    "q": "Inside an `async` function, the code that runs after an `await` is scheduled as…",
    "choices": [
      "A microtask - it behaves like a `.then` callback and runs before pending timers",
      "A macrotask - it waits behind every queued setTimeout",
      "Synchronous code - it runs immediately with no deferral",
      "A new thread that runs in parallel"
    ],
    "answer": 0,
    "explain": "await suspends the function and queues its continuation as a microtask once the awaited value settles. That's why the line after await runs ahead of a pending setTimeout - under the hood it's a promise reaction."
  }
]
```


---

# Functional JavaScript - Functions as Building Blocks

You already use functional JavaScript without naming it: every time you wrote `nums.map(n => n * 2)` back in [Phase 9](09-idioms-and-gotchas.md), you handed one function to another. That move is the seed the whole functional style grows from - a *way of thinking*: build programs from small, predictable functions and snap them together, for code you can test in isolation, reason about without tracing the whole program, and change without fear. Five ideas carry the weight: functions as values, purity, higher-order functions, immutability, and composition. Each builds on the last.

## Functions are first-class values

**What it actually is.** In JavaScript a function is an ordinary value: store it in a variable, put it in an array, pass it as an argument, return it from another function. Everything else in this phase follows from that.

📝 **First-class value** - something the language lets you store in a variable, pass as an argument, and return from a function. Functions qualify in JavaScript; that's what "functions are first-class" means.

```javascript runnable
const double = (n) => n * 2;        // store a function in a variable
const ops = [double, (n) => n + 1]; // put functions in an array

function applyAll(value, fns) {     // accept functions as an argument
  return fns.map((fn) => fn(value));
}

console.log(double(5));
console.log(applyAll(10, ops));
```
```console
10
[ 20, 11 ]
```
*What just happened:* `double` is a function living in a variable, no different from `const x = 5`. Nothing here is special syntax - we're treating functions as plain values, and passing/returning them is the foundation the next four sections stand on.

## Pure functions

The most valuable function you can write is one that's *boring and predictable* - a **pure function**.

📝 **Pure function** - a function that (1) returns the same output for the same input, every time, and (2) has no side effects: it doesn't change anything outside itself (no mutating shared variables, no writing to the page, no network calls, no `console.log`). Give it `2` and `3`, it gives back `5` - today, tomorrow, on any machine.

**Why this matters.** A pure function is a closed box: to understand it you only read *it*. Test it with nothing but inputs and expected outputs - no setup, no mocks, no database - and it can never surprise a caller by quietly editing something elsewhere. Impure functions depend on or alter the world around them, so understanding one means understanding everything it touches.

```javascript runnable
// PURE: output depends only on inputs; nothing outside changes.
function addPure(a, b) {
  return a + b;
}

// IMPURE: reads and writes a shared variable outside itself.
let total = 0;
function addImpure(n) {
  total += n;          // side effect: mutates outer state
  return total;        // output depends on history, not just input
}

console.log(addPure(2, 3), addPure(2, 3)); // same input -> same output
console.log(addImpure(5), addImpure(5));   // same input -> DIFFERENT output
```
```console
5 5
5 10
```
*What just happened:* `addPure(2, 3)` returned `5` both times - same inputs, same answer, forever. `addImpure(5)` returned `5` then `10`, because it secretly leans on and mutates `total`. Same input, different output: the hallmark of an impure function, and why it's harder to test and trust.

💡 **Push side effects to the edges.** You can't avoid them entirely - a real program must eventually read input, draw to the screen, or save a file. The functional move is to *concentrate* them: keep a large core of pure functions that compute results, and do the messy I/O in a thin shell at the boundary.

## Higher-order functions

Once functions are values, a natural superpower appears: functions that take or return *other* functions.

📝 **Higher-order function** - a function that takes a function as an argument, or returns one. `map`, `filter`, and `reduce` from Phase 9 all take a function. Here you'll also build one that *returns* a function.

A function that returns a function is a **factory**: give it some configuration and it hands back a brand-new, specialized function with that configuration baked in.

```javascript runnable
// A factory: returns a NEW function specialized by `factor`.
function multiplyBy(factor) {
  return (n) => n * factor;   // the returned function remembers `factor`
}

const triple = multiplyBy(3);
const tenfold = multiplyBy(10);

console.log(triple(5));
console.log(tenfold(5));
console.log([1, 2, 3].map(multiplyBy(2))); // hand the new function to map
```
```console
15
50
[ 2, 4, 6 ]
```
*What just happened:* `multiplyBy(3)` *returned a function* - one that multiplies by 3 because it remembers `factor` from the call that created it (a closure, from [Phase 10](10-scope-and-closures.md)). The last line shows why this is useful: `multiplyBy(2)` produces exactly the single-argument function `map` wants, built on the spot and handed straight over.

Returning functions also lets you *wrap* behavior. A logging wrapper takes any function and returns a new one that does the same job, plus logs:

```javascript runnable
function withLogging(fn) {
  return (...args) => {              // rest gathers all arguments (Phase 9)
    console.log("calling with:", args);
    const result = fn(...args);      // spread them back in
    console.log("got:", result);
    return result;
  };
}

const add = (a, b) => a + b;
const loudAdd = withLogging(add);
loudAdd(2, 3);
```
```console
calling with: [ 2, 3 ]
got: 5
```
*What just happened:* `withLogging` returned a *new* function that wraps the original - printing before and after - without `add` itself ever changing. This "take a function, return an enhanced function" pattern is the heart of decorators, middleware, and a lot of library design.

## Immutability - don't mutate, return new data

Back in Phase 9 you met the reference trap: objects and arrays are held by *reference*, so two variables can point at the same object and a change through one is visible through the other. **Immutability** defuses that trap: instead of changing data in place, you produce *new* data and leave the original untouched.

📝 **Immutability** - treating data as read-only. Rather than mutating an array or object (`push`, `splice`, `obj.x = ...`), build a new one (`map`, `filter`, spread `...`) and leave the original alone.

**Why bother.** Shared mutable state causes an enormous share of bugs: when any part of the program can reach in and change an object another part relies on, behavior depends on *who ran when*. If data never changes out from under you, a whole category of "why did this value change?!" bugs can't happen. (Pure functions and immutability are siblings: a pure function won't mutate its inputs, so it naturally produces new data.)

Here's the trap and the fix side by side:

```javascript runnable
const original = [1, 2, 3];

// MUTATING approach: push changes `original` in place.
function addItemBad(arr, item) {
  arr.push(item);   // mutates the array passed in!
  return arr;
}

const bad = addItemBad(original, 4);
console.log("after bad:", original); // original was modified - surprise

// IMMUTABLE approach: build a new array, leave the input alone.
const fresh = [10, 20, 30];
function addItemGood(arr, item) {
  return [...arr, item]; // new array; arr is untouched
}

const good = addItemGood(fresh, 40);
console.log("fresh stays:", fresh);
console.log("good is new: ", good);
```
```console
after bad: [ 1, 2, 3, 4 ]
fresh stays: [ 10, 20, 30 ]
good is new:  [ 10, 20, 30, 40 ]
```
*What just happened:* `addItemBad` called `push`, mutating the very array it was handed - `original` silently grew a `4` the caller never asked for. `addItemGood` instead spread the old items into a *new* array, leaving `fresh` untouched. The immutable version can't corrupt its caller's data, so it's safe to pass around freely.

The same pattern works for objects with spread, and for "removing" items with `filter`:

```javascript runnable
const user = { name: "Ada", role: "user" };

const promoted = { ...user, role: "admin" }; // new object, one field changed
const numbers = [1, 2, 3, 4];
const noTwo = numbers.filter((n) => n !== 2); // new array without the 2

console.log("user unchanged:", user);
console.log("promoted:      ", promoted);
console.log("noTwo:         ", noTwo);
```
```console
user unchanged: { name: 'Ada', role: 'user' }
promoted:       { name: 'Ada', role: 'admin' }
noTwo:          [ 1, 3, 4 ]
```
*What just happened:* the spread copied every field of `user` and overrode `role` without touching `user`; `filter` built a new array without the `2`, never touching `numbers`. Rule of thumb: reach for `map`, `filter`, and spread (which return new data) instead of `push`, `splice`, and direct property assignment (which mutate).

## Composition (and a taste of currying)

**Composition** builds a bigger function by chaining small ones, so the output of each feeds the next. If your functions are pure, this is safe: no hidden state to trip over, so a pipeline is just "do this, then this, then this."

📝 **Composition** - combining simple functions into a more complex one by feeding each function's output into the next. `pipe(f, g)(x)` means `g(f(x))`: run `f` on `x`, then `g` on the result.

A tiny `pipe` is itself a higher-order function that takes functions and returns a function:

```javascript runnable
const pipe = (...fns) => (x) => fns.reduce((acc, fn) => fn(acc), x);

const trim = (s) => s.trim();
const lower = (s) => s.toLowerCase();
const exclaim = (s) => s + "!";

const shout = pipe(trim, lower, exclaim); // left-to-right pipeline

console.log(shout("  HELLO  "));
```
```console
hello!
```
*What just happened:* `reduce` ran the three functions left to right against `"  HELLO  "`: `trim`, then `lower`, then `exclaim`. (Mathematicians write composition right-to-left and call it `compose`; `pipe` is the same idea in reading order, which most people find clearer.)

**A taste of currying.** **Currying** turns a function that takes several arguments into a chain of functions that each take one - the shape you already saw in `multiplyBy`: call it with one argument now, get a function waiting for the rest. It's handy because pipelines and `map` want single-argument functions.

```javascript runnable
// Curried: take `factor` now, return a function waiting for `n`.
const multiply = (factor) => (n) => n * factor;
const add = (amount) => (n) => n + amount;

const pipe = (...fns) => (x) => fns.reduce((acc, fn) => fn(acc), x);

const transform = pipe(multiply(2), add(10)); // tidy single-arg functions
console.log(transform(5)); // (5 * 2) + 10
```
```console
20
```
*What just happened:* `multiply(2)` and `add(10)` are each pre-configured to a single argument, so they slot straight into `pipe` and read as a pipeline: double, then add ten.

⚠️ **Don't over-engineer.** It's tempting to take this far - deep `pipe` chains, everything curried, "point-free" code with no named intermediate values. Resist it when it hurts readability: the goal is code that's *easier* to understand, not a puzzle. A two-line composition the next person can read beats a clever one-liner they have to decode.

## Recap

1. **Functions are first-class values** - store, pass, and return them. The foundation the entire functional style is built on.
2. **Pure functions** return the same output for the same input and cause no side effects, making them trivial to test and impossible to surprise you. Push unavoidable side effects to the edges.
3. **Higher-order functions** take or return functions. Factories (return a function) and wrappers (take and enhance a function) generate and extend behavior without rewriting it.
4. **Immutability**: build new data (`map`, `filter`, spread) instead of mutating in place (`push`, `splice`, assignment) - defusing the shared-reference bugs from Phase 9.
5. **Composition** chains small functions into bigger ones (`pipe`); **currying** pre-configures functions to single arguments so they snap together.
6. ⚠️ Use these to make code *clearer*, not cleverer. Readability beats point-free wizardry.

## Quick check

Test yourself on the ideas that make functional code predictable:

```quiz
[
  {
    "q": "Which function is pure?",
    "choices": [
      "`function add(a, b) { return a + b; }`",
      "`function add(n) { total += n; return total; }` (total is an outer variable)",
      "`function save(x) { localStorage.setItem('x', x); }`",
      "`function now() { return Date.now(); }`"
    ],
    "answer": 0,
    "explain": "A pure function returns the same output for the same input and has no side effects. `add(a, b)` depends only on its arguments and changes nothing outside itself. The others mutate outer state, write to storage, or return a value that depends on the clock rather than the input."
  },
  {
    "q": "Why prefer `return [...arr, item]` over `arr.push(item)` when adding to an array?",
    "choices": [
      "Spread builds a new array and leaves the original untouched, avoiding shared-reference bugs",
      "`push` is deprecated in modern JavaScript",
      "Spread is always faster than push",
      "`push` cannot add to the end of an array"
    ],
    "answer": 0,
    "explain": "`push` mutates the array in place, so it can silently change data a caller is still relying on (the reference trap). Spreading into a new array leaves the input alone, which is the immutable approach and prevents a whole class of \"why did this change?\" bugs."
  },
  {
    "q": "Given `const pipe = (...fns) => (x) => fns.reduce((acc, fn) => fn(acc), x);`, what does `pipe(f, g)(2)` compute?",
    "choices": [
      "`g(f(2))` - run f on 2, then run g on the result",
      "`f(g(2))` - run g first, then f",
      "`f(2) + g(2)` - run both on 2 and add the results",
      "`[f(2), g(2)]` - an array of both results"
    ],
    "answer": 0,
    "explain": "`pipe` runs the functions left to right, feeding each output into the next. So `pipe(f, g)(2)` applies `f` to `2`, then applies `g` to that result: `g(f(2))`. That left-to-right reading order is exactly why `pipe` is often clearer than mathematical right-to-left `compose`."
  }
]
```


---

# Modules & Bundlers, Deep - From Files to a Shippable App

Back in [Phase 5](05-modules-and-project-layout.md) you split code across files with `import` and `export`,
and [Phase 8](08-ecosystem-and-tooling.md) name-dropped "bundlers." This phase is the *why*: what the two
competing module systems actually are, why one lets your tools do near-magical things, and what a bundler
really does when you run `npm run build`.

One property ties it together: **static structure**. Modern JavaScript modules declare their dependencies in
a way tools can read *without running the code*, unlocking optimizations - shrinking your app, splitting it
into pieces, loading heavy bits only when needed. See that, and the bundler stops being a black box.

## Two module systems - ESM vs CommonJS

JavaScript had no built-in way to share code across files, so the community invented one (**CommonJS**), and
years later the language got an official one (**ES Modules**). You'll meet both.

📝 **ES Modules (ESM)** - the *standard* module system, built into the language and browsers. Uses `import`
and `export`. Imports are **static**: declared at the top level, resolved before the code runs.

📝 **CommonJS (CJS)** - the *older* module system from early Node.js. Uses `require()` and `module.exports`.
Imports are **dynamic**: `require()` is a regular function call that runs at runtime, wherever you put it.

Side by side, first ESM:

```javascript
// math.js - ESM
export function add(a, b) {
  return a + b;
}
export const PI = 3.14159;

// app.js - ESM
import { add, PI } from "./math.js";
console.log(add(2, 3), PI);
```

Now the same thing in CommonJS:

```javascript
// math.js - CommonJS
function add(a, b) {
  return a + b;
}
module.exports = { add, PI: 3.14159 };

// app.js - CommonJS
const { add, PI } = require("./math.js");
console.log(add(2, 3), PI);
```

Both export an `add` function and a `PI` constant and import them elsewhere - the *intent* is identical. The
difference is mechanical: ESM uses dedicated `import`/`export` keywords at the top of the file, while
CommonJS uses a plain function call (`require`) assigned to a special `module.exports` object. That
distinction looks cosmetic, but it's the whole ballgame, as the next section shows.

⚠️ **Gotcha - don't mix them carelessly.** You can't drop `import` statements into a CommonJS-mode file, and
`require()`-ing an ESM file only works in newer Node and still fails if that module uses top-level `await`.
Node decides a file's mode from `package.json`'s `"type"` field (`"module"` = ESM) or
the extension (`.mjs` = ESM, `.cjs` = CommonJS). "Cannot use import statement outside a module" or "require
is not defined" means you've crossed the streams. Prefer ESM for new code.

## Why static structure changes everything

The key difference: **ESM imports are knowable without running the program.**

`import { add } from "./math.js"` must sit at the top level and can't be hidden inside an `if` or built from a
variable, so a tool can read your files as plain text and draw the complete **dependency graph** before a
single line executes.

CommonJS can't promise that: `require()` is an ordinary function, so it can appear anywhere and take a
computed argument.

```javascript
// Perfectly legal CommonJS - and impossible to analyze statically
const name = Math.random() > 0.5 ? "./mathA.js" : "./mathB.js";
const lib = require(name); // which file? nobody knows until it runs
```

The module loaded here is decided *at runtime* by a coin flip - no tool can know whether `mathA` or `mathB`
is needed without running the program. That single bit of dynamism poisons the well: tools must assume the
worst and keep everything.

💡 **Insight.** Static structure is a promise to your tools: every dependency is declared up front. That
promise unlocks the optimizations in the rest of this phase. ESM made it; CommonJS, by design, can't.

## Tree-shaking - dropping code you never use

📝 **Tree-shaking** - dead-code elimination for modules: the bundler drops any `export` nothing in your app
actually `import`s, so the unused code never makes it into the final file.

Think of your dependency graph as a tree: shake it and the dead leaves - exports no one reached for - fall
off. Pull in one helper from a library exporting fifty, and a tree-shaking bundler ships only that one (plus
its dependencies).

```javascript
// utils.js - exports three things
export function used() {
  return "I'm in the bundle";
}
export function neverCalled() {
  return "I should be dropped";
}
export function alsoUnused() {
  return "me too";
}

// app.js - imports exactly one
import { used } from "./utils.js";
console.log(used());
```

`app.js` imports only `used`. Since ESM lets the bundler see the entire import graph ahead of time, it can
prove `neverCalled` and `alsoUnused` are unreachable and drop them from the shipped bundle - smaller file,
less to download and parse.

This is why CommonJS resists tree-shaking: `module.exports = { ... }` builds a plain object at runtime, and
any code could later read an arbitrary key off it. The bundler can't prove a given export is unused, so it
keeps everything. **Tree-shaking needs ESM's static structure to work.**

💡 **Insight - import only what you use.** Reach for named imports of specific things you need
(`import { debounce } from "lodash-es"`) rather than a whole namespace, and prefer libraries shipping ESM -
the bundler gets a clearer picture and rewards you with a leaner app.

## What a bundler actually does

A **bundler** starts at your **entry file** (say `app.js`), reads its imports, follows each to the file it
points at, and keeps walking until it has discovered every module your app touches, then stitches them into
one (or a few) optimized files the browser can load efficiently.

📝 **Bundler** - a build tool that follows your import graph from an entry point, then combines and
transforms those modules into a small number of optimized output files.

Along the way it does more than concatenate:

- **Resolves** every import path to a real file (including into `node_modules`).
- **Transforms** code - compiles TypeScript or JSX, converts modern syntax for older browsers.
- **Tree-shakes** away unused exports (the previous section).
- **Minifies** - strips whitespace, shortens variable names, removes comments.
- **Bundles** the survivors into output files, often with content hashes in the name for caching.

```mermaid
flowchart LR
  A[app.js<br/>entry] --> D[Bundler]
  B[utils.js] --> D
  C[node_modules] --> D
  D --> E[bundle.js<br/>minified · tree-shaken]
```

Why bother? The browser can't follow a giant web of tiny `import` requests efficiently - historically each
was a separate round-trip, and fewer, well-organized files still load faster. It also doesn't understand
TypeScript, JSX, or the newest syntax, so the bundler converts your code into something it *does* understand.

The popular tools today - **Vite**, **esbuild**, **webpack**, and others - do this same core job, differing in
speed, configuration, and defaults, not purpose. Vite leans on esbuild and is the default for new projects;
webpack is the veteran in older codebases. The mental model carries across all of them.

## Dynamic import & code splitting

Everything so far loads at startup. But some code - a giant charting library, an admin-only screen, a rarely
opened dialog - isn't needed the moment the page loads, and forcing it on the user slows the app's start.

The fix is **`import()`** as a *function* (note the parentheses). Unlike the static `import` statement, this
runs at runtime, returns a **promise**, and tells the bundler to split whatever it points at into its own
file, not loaded until this line runs.

```javascript
// Load a heavy module only when the user clicks the button
button.addEventListener("click", async () => {
  const { renderChart } = await import("./heavy-chart.js");
  renderChart(data);
});
```

`heavy-chart.js` is **not** in the initial bundle. Static analysis still works: the bundler sees the
`import()` call and carves `heavy-chart.js` (and its dependencies) into a separate **chunk**, downloaded only
when the click handler runs and awaits the promise. Startup payload stays small; the heavy code arrives when
first needed.

📝 **Code splitting** - breaking your app into multiple bundles ("chunks") so the browser downloads each
piece on demand instead of one giant file. `import()` marks a split point.

This is the standard pattern behind "lazy-loaded routes" in frameworks: each page becomes its own chunk, so
visiting the home page doesn't also download the settings page, checkout flow, and admin panel.

⚠️ **Gotcha - don't over-split.** Each chunk is a separate network request with its own overhead, so
shattering your app into hundreds of tiny chunks can be *slower* than one reasonable bundle. Split on real
boundaries - distinct routes, genuinely heavy libraries, rarely-touched features - not on every module. The
goal is "load less at startup," not "load everything in maximum pieces."

## Recap

1. **ESM** (`import`/`export`) is the standard, *static* module system; **CommonJS** (`require`/
   `module.exports`) is the older, *dynamic* one from Node. Prefer ESM for new code.
2. ESM's imports are **knowable without running the code**, so tools can build the full dependency graph
   ahead of time - the foundation everything else stands on.
3. **Tree-shaking** drops exports nothing imports, shrinking your bundle; it needs ESM's static structure,
   which is why CommonJS can't be reliably tree-shaken.
4. A **bundler** (Vite, esbuild, webpack) follows your import graph from an entry file, then resolves,
   transforms, tree-shakes, minifies, and combines everything into a few optimized output files.
5. **`import()`** loads a module at runtime and returns a promise, letting the bundler split heavy or rare
   code into separate **chunks** loaded on demand - but split on real boundaries, not every file.

## Quick check

Test yourself on the idea that powers this phase - static structure and what it buys you:

```quiz
[
  {
    "q": "Why can ESM be tree-shaken reliably but CommonJS generally can't?",
    "choices": [
      "ESM imports are static and declared up front, so tools can prove which exports are unused before running the code",
      "ESM files are always smaller than CommonJS files",
      "CommonJS code is written in an older version of JavaScript that bundlers refuse to read",
      "Tree-shaking only works in the browser, and CommonJS only runs in Node"
    ],
    "answer": 0,
    "explain": "Tree-shaking depends on the bundler knowing the full import graph without executing the program. ESM's static, top-level imports make that possible; CommonJS's runtime `require()` can take computed paths, so the bundler must keep everything to stay correct."
  },
  {
    "q": "What does the bundler do when it sees `await import(\"./heavy-chart.js\")`?",
    "choices": [
      "Splits heavy-chart.js into a separate chunk that downloads only when that line runs",
      "Inlines heavy-chart.js into the main bundle so it loads at startup",
      "Throws an error because import() isn't valid JavaScript",
      "Deletes heavy-chart.js from the project as dead code"
    ],
    "answer": 0,
    "explain": "The dynamic `import()` form is a code-split point. The bundler carves that module (and its dependencies) into its own chunk, and the browser fetches it on demand when the line executes - keeping the startup payload small."
  },
  {
    "q": "Which best describes the core job of a bundler like Vite or webpack?",
    "choices": [
      "Follow the import graph from an entry file and combine/transform the modules into a few optimized output files",
      "Run your tests and report which ones fail",
      "Format your code and fix indentation on save",
      "Download npm packages and add them to package.json"
    ],
    "answer": 0,
    "explain": "A bundler starts at an entry point, follows every import to build the dependency graph, then resolves, transforms, tree-shakes, minifies, and combines those modules into optimized files the browser can load efficiently."
  }
]
```


---

# Performance & Memory - How V8 Runs Your Code

You've trusted the engine to make your JavaScript fast. This phase pulls back that curtain - not to make you micro-optimize every line (mostly a trap), but to give you an accurate **mental model**, turning "fast vs. slow" from folklore into something you can reason about.

Two ideas drive this phase. First: V8 (the engine inside Chrome and Node) *watches* your code and rewrites hot parts into machine code rather than plodding through line by line. Second: memory you stop using gets cleaned up automatically - until, through a few classic mistakes, it doesn't.

## How V8 actually runs your code

The naive story - "JavaScript is interpreted, so the engine reads each line and does what it says, every time" - is half true at startup and wrong for code that runs a lot.

📝 **JIT (Just-In-Time) compiler** - a compiler that runs *while your program runs*: it interprets quickly at first, watches which functions get called over and over ("hot" code), then compiles those into optimized machine code on the fly - so the parts that matter run at near-native speed.

The real flow: source is parsed and handed to a fast interpreter (Ignition) so the program starts immediately, no waiting for a full compile. V8 counts how often each function is called and what types it sees; once hot, the optimizing compiler (TurboFan) compiles a specialized, fast version *based on the types observed so far*. (Today's V8 slots two more tiers between these - Sparkplug, a quick baseline compiler, and Maglev, a mid-tier optimizer - but the two-endpoint story, interpret then optimize, is the model that matters.)

```mermaid
flowchart LR
  A[Source code] --> B[Parse]
  B --> C["Interpret<br/>(Ignition) - fast start"]
  C -->|function gets hot| D["Optimize<br/>(TurboFan) - fast machine code"]
  D -->|types stop matching| E[Deoptimize - back to C]
  E --> C
```

That last arrow is the catch. TurboFan's fast code is built on an *assumption* - "this function always gets numbers" (or objects shaped a certain way). Break that assumption and V8 throws away the optimized code and falls back to the interpreter: a **deoptimization**, and the opposite of free.

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

// "Warm up" add() with consistent number types, then time many calls.
function timeAdd(prep) {
  prep();                                    // force whatever types we want
  const start = performance.now();
  let acc = 0;
  for (let i = 0; i < 5_000_000; i++) acc += add(i, i + 1);
  return performance.now() - start;
}

const monomorphic = timeAdd(() => { for (let i = 0; i < 100; i++) add(i, i); });
const chaotic     = timeAdd(() => { add(1, 2); add("a", "b"); add({}, []); add(true, 1); });

console.log("steady numbers (ms):", monomorphic.toFixed(1));
console.log("mixed types   (ms):", chaotic.toFixed(1));
```
```console
steady numbers (ms): 9.4
mixed types   (ms): 18.7
```
*What just happened:* Both loops call the same `add` with the same arithmetic. The first warmed `add` with only numbers, so V8 compiled a tight number-only version; the second fed it strings, objects, and booleans first, so it compiled a more defensive, slower version (or kept deoptimizing). Same code, measurably different speed. (Timings vary by machine and engine version, and a clever engine may even optimize this toy away - the *direction* is the lesson, not the exact numbers.)

💡 **Practical takeaway:** you don't need to think about TurboFan day to day - just keep hot functions *predictable*, with consistent types and object shapes. "Monomorphic" (one shape) code is what the optimizer loves; chaotic, shape-shifting code forces it to give up.

## Hidden classes - why object shape matters

V8 craves the same predictability with *objects*. They feel like loose bags of key-value pairs you can reshape at will, but V8 quietly assigns every object a **hidden class** (a "shape" or "map") describing its layout: which properties, in which order, at which memory offsets.

📝 **Hidden class / shape** - V8's internal record of an object's properties and their order. Two objects built the *same way* share one hidden class, letting V8 generate fast, direct property access instead of a slow dictionary lookup.

Objects sharing a hidden class let V8 compile a property read like `point.x` down to "grab the value at offset 0" - one instruction. *Different* shapes flowing through the same code force a much slower dictionary-style lookup instead.

The trap: you can change an object's shape without realizing it, and **the order you add properties is part of the shape**.

```javascript runnable
// Same fields, different insertion order → two different hidden classes.
function makeA() { const o = {}; o.x = 1; o.y = 2; return o; }
function makeB() { const o = {}; o.y = 2; o.x = 1; return o; }

// Consistent shape: build the object with all fields at once.
function makeFast() { return { x: 1, y: 2 }; }

const a = makeA();
const b = makeB();
console.log("same keys & values:", a.x === b.x && a.y === b.y); // true
console.log("but V8 sees A and B as different shapes internally");
console.log("makeFast gives every object the same shape:", makeFast());
```
```console
same keys & values: true
but V8 sees A and B as different shapes internally
makeFast gives every object the same shape: { x: 1, y: 2 }
```
*What just happened:* `a` and `b` hold identical data, but `makeA` added `x` then `y` while `makeB` added `y` then `x`, so V8 built two separate hidden classes - any function processing both sees *two* shapes and can't fully specialize. `makeFast` sidesteps this by creating every field in one literal, so every object it returns is the same shape.

⚠️ **Gotcha - reshaping objects after creation forces slow paths.** Adding properties in different orders, adding fields conditionally (`if (x) obj.z = ...`), or `delete obj.key` all create new hidden classes or knock an object into slow "dictionary mode." `delete` is especially nasty: it can permanently demote an object even after it's only used for reads.

💡 **The fix is a habit, not a tool:** initialize objects with *all* fields up front, in a consistent order, even if some start as `null` or `0`. Instead of adding `obj.error` only when something fails, declare `error: null` from the start.

## Algorithmic cost dominates micro-optimizations

The most important performance lesson here has nothing to do with V8 internals: **the algorithm you choose almost always matters more than how cleverly you write the lines.**

Beginners obsess over shaving operations - "is `for` faster than `forEach`? Should I cache `arr.length`?" - but these differences are usually noise. A single `O(n²)` loop hiding in your code dwarfs every micro-optimization once data grows. The classic culprit: searching inside a loop.

```javascript runnable
// Find which of `needles` exist in `haystack`.
const haystack = Array.from({ length: 20000 }, (_, i) => i);
const needles  = Array.from({ length: 20000 }, (_, i) => i * 2);

// Approach 1: nested lookup with .includes() - O(n²).
let t0 = performance.now();
let found1 = 0;
for (const n of needles) {
  if (haystack.includes(n)) found1++;   // .includes scans the whole array each time
}
const slow = performance.now() - t0;

// Approach 2: build a Set once, then look up - O(n).
let t1 = performance.now();
const set = new Set(haystack);          // one pass to build
let found2 = 0;
for (const n of needles) {
  if (set.has(n)) found2++;             // O(1) average lookup
}
const fast = performance.now() - t1;

console.log("includes() in a loop (ms):", slow.toFixed(1), "found", found1);
console.log("Set lookup          (ms):", fast.toFixed(1), "found", found2);
console.log("speedup:", (slow / fast).toFixed(0) + "x");
```
```console
includes() in a loop (ms): 412.0 found 10000
Set lookup          (ms): 2.3 found 10000
speedup: 179x
```
*What just happened:* Both versions get the identical answer, but the first calls `haystack.includes(n)` inside a loop, and `includes` itself scans the array - roughly 20,000 × 20,000 = 400 million comparisons. The second builds a `Set` once, so each `set.has(n)` is near-instant. Not 10% faster - *orders of magnitude* faster, and the gap widens as data grows; no loop-tuning could rescue the first approach. (Exact timings vary by machine; the ratio is what matters.)

Play with how operation counts explode as input grows - the intuition that makes you reach for a `Map` or `Set` reflexively:

```playground-bigo
```

💡 **The order of operations for performance work:** pick the right data structure and algorithm first (turn `O(n²)` into `O(n)`); only *then*, if you've measured and it's still too slow, worry about constant-factor tweaks. Micro-optimizing before fixing a bad algorithm is polishing a part you're about to throw away.

## Garbage collection - memory you stop using comes back

In languages like C, you ask the system for memory and must hand it back yourself; forget to, and you leak. JavaScript has a **garbage collector** that finds unused memory and reclaims it automatically. You allocate by creating objects; you "free" by *letting go* of them.

📝 **Garbage collection (GC)** - the engine automatically reclaiming memory that your program can no longer reach. You never call `free()`. When nothing references an object anymore, it becomes eligible to be collected.

The mental model that matters is **reachability**. Start from the "roots" - global variables, the current call stack, things actively in scope - and follow every reference. Anything reachable from a root is *alive*; anything you *can't* reach is garbage, free for the collector to reclaim. It doesn't matter whether you "meant" to keep it - only whether a chain of references still leads to it.

```javascript runnable
function makeBigThing() {
  // A chunky object. While someone references it, it stays alive.
  return { data: new Array(100_000).fill("x"), id: Math.random() };
}

let ref = makeBigThing();        // `ref` is a root → the object is reachable
console.log("alive, id:", ref.id.toFixed(4));

ref = null;                      // dropped the only reference → now unreachable
console.log("ref is now:", ref, "→ the big object is eligible for GC");
// You can't force collection from JS, but the engine will reclaim it later.
```
```console
alive, id: 0.7321
ref is now: null → the big object is eligible for GC
```
*What just happened:* `makeBigThing()` allocated a sizable object, and `ref` pointed at it, making it reachable and keeping it alive. Setting `ref = null` cut the only reference: now unreachable, it's garbage, and the collector reclaims it whenever it next runs. You never freed anything - you just stopped referencing it, and that's the whole job.

Watch reachability and collection play out visually - see objects go from rooted, to orphaned, to swept away:

```playground-gc
```

💡 V8's collector is *generational*: it assumes most objects die young (a temporary object inside a function call, gone the moment the call returns) and collects that "young generation" cheaply and often. Survivors get promoted to an "old generation" scanned less frequently. Upshot: short-lived temporary objects are cheap - don't fear creating them.

## Memory leaks in a garbage-collected language

If memory is reclaimed automatically, how can you leak it? The collector only reclaims what's **unreachable**. A JavaScript leak isn't forgetting to free - it's *accidentally keeping a reference alive* so the collector thinks the object is still needed. Memory grows, nothing gets reclaimed, and eventually the tab freezes or the Node process gets killed.

Three classic ways it happens:

- **Forgotten timers and listeners.** A `setInterval`, or an `addEventListener` never removed, keeps a reference to its callback - and the callback keeps everything it closes over.
- **Growing global caches.** A module-level `Map` or array you keep pushing into but never trim is reachable forever (it's a root), so everything inside lives forever too.
- **Closures capturing big objects.** A closure holds every variable it references; a long-lived function capturing a huge object it doesn't need prevents that object's collection.

Here's the most common one - a cache that only grows - plus the fix:

```javascript runnable
// LEAK: a cache that never forgets. Every key lives forever.
const leakyCache = new Map();
function getLeaky(key) {
  if (!leakyCache.has(key)) leakyCache.set(key, { data: new Array(1000).fill(key) });
  return leakyCache.get(key);
}
for (let i = 0; i < 5000; i++) getLeaky(i);     // 5000 entries, all retained
console.log("leaky cache size:", leakyCache.size); // grows without bound

// FIX: bound the cache - evict the oldest entry past a limit.
const MAX = 100;
const boundedCache = new Map();
function getBounded(key) {
  if (!boundedCache.has(key)) {
    boundedCache.set(key, { data: new Array(1000).fill(key) });
    if (boundedCache.size > MAX) {
      const oldest = boundedCache.keys().next().value; // Maps keep insertion order
      boundedCache.delete(oldest);                     // let the old entry be collected
    }
  }
  return boundedCache.get(key);
}
for (let i = 0; i < 5000; i++) getBounded(i);
console.log("bounded cache size:", boundedCache.size); // capped at MAX
```
```console
leaky cache size: 5000
bounded cache size: 100
```
*What just happened:* `leakyCache` is a module-level `Map` - a root - so every entry is reachable forever, even once you'll never use that key again: a steady upward memory creep until something dies. `boundedCache` fixes it by capping the size - once full, adding a new entry deletes the oldest, dropping the only reference so the collector *can* reclaim it. Same idea for timers (`clearInterval` when done) and listeners (`removeEventListener` when the element goes away).

⚠️ **Measure before you optimize - guessing wastes time.** Don't *assume* where a leak or slowdown is: use the browser DevTools **Memory** tab (heap snapshots over time, watch for objects that keep growing) and the **Performance** tab to profile what's actually slow. Engineers burn staggering hours "optimizing" code that was never the bottleneck - profile first, fix the real thing, then verify the number moved.

For a deeper, math-free tour of why `O(n²)` and `O(n)` diverge the way they do, see [Big-O without the math panic](/guides/big-o-without-the-math-panic).

## Recap

1. **V8 uses a JIT compiler:** it starts by interpreting, watches for *hot* code, and compiles it to fast machine code based on types seen so far. **Consistent types** keep hot functions fast; mixed types force a **deoptimization**.
2. **Object shape matters.** V8 assigns each object a **hidden class** based on its properties *and their order*. Build objects with all fields up front, in a consistent order; avoid `delete` and conditional property-adding on hot objects.
3. **Algorithm beats micro-optimization.** Turning an `O(n²)` nested scan into an `O(n)` `Set`/`Map` lookup can be 100×+ faster - far more than any line-level tweak. Fix the algorithm first.
4. **Garbage collection is automatic**, based on **reachability**: an object lives as long as a chain of references reaches it from a root. You "free" memory by *letting go* of references, never by calling `free()`.
5. **Leaks still happen** when you accidentally keep references alive - forgotten timers/listeners, unbounded global caches, closures holding big objects. Fix: *drop* the reference (clear the timer, bound the cache, remove the listener).
6. **Measure, don't guess.** Use DevTools' Memory and Performance tabs to find the real bottleneck before optimizing, and verify the fix actually helped.

## Quick check

Test yourself on the ideas that change how you write code - predictable shapes, algorithmic cost, and reachability:

```quiz
[
  {
    "q": "Why does calling the same function with consistently typed arguments tend to run faster in V8?",
    "choices": [
      "V8's JIT can compile a specialized, optimized version based on the types it observes; mixing types forces it to deoptimize to slower, defensive code",
      "Consistent types use less memory, so the garbage collector runs less often",
      "V8 caches the function's return value when the arguments have the same type",
      "Typed arguments skip the parser entirely"
    ],
    "answer": 0,
    "explain": "The JIT optimizes hot functions based on the types it has seen. Stable (monomorphic) types let it keep the fast machine-code version; feeding it varied types breaks its assumptions and triggers deoptimization back to the slower interpreter."
  },
  {
    "q": "You need to check membership repeatedly while looping over a large list. Which is the bigger win?",
    "choices": [
      "Replacing `array.includes(x)` inside the loop with a `Set` built once and `set.has(x)` - turning O(n²) into O(n)",
      "Caching `array.length` in a variable before the loop",
      "Switching the `for...of` loop to a plain indexed `for` loop",
      "Using `let` instead of `const` for the loop counter"
    ],
    "answer": 0,
    "explain": "Algorithmic cost dominates. `includes` in a loop is O(n²) because it rescans the array each time; a `Set` lookup is O(1) average, making the whole thing O(n). That can be 100×+ faster - the other options are constant-factor noise by comparison."
  },
  {
    "q": "In a garbage-collected language like JavaScript, how does a memory leak typically happen?",
    "choices": [
      "You accidentally keep an object reachable - e.g. an unbounded global cache or a timer that's never cleared - so the collector never reclaims it",
      "You forget to call free() on objects you allocated",
      "You create too many short-lived temporary objects inside functions",
      "The garbage collector has a bug and skips certain objects"
    ],
    "answer": 0,
    "explain": "GC reclaims only unreachable memory. A leak means something still references the object - a growing global Map, a forgotten setInterval, a closure holding a big value - so it stays 'alive.' The fix is to drop the reference. Short-lived temporaries are cheap and fine."
  }
]
```


---

# Types & the Road to TypeScript - Catching Bugs Before They Run

This final phase covers the single biggest upgrade left on the table - not a new language feature, but a different way of *checking* it before it ever runs.

**JavaScript trusts you completely**: it will happily add a number to a string, call a function with the wrong arguments, or read a property off `undefined`, complaining (if at all) only when the broken line executes. A type checker is a second pair of eyes that reads your code *without running it* and points at mistakes while you're still typing.

## The cost of dynamic typing

📝 **Dynamic typing** - the *types* of your values (number, string, object…) are tracked and checked only while the program runs. A variable can hold a string now and a number a second later, and nothing checks the pieces fit together until execution reaches them.

That flexibility is nice for sketching things out fast, but a whole category of mistakes - typos in property names, wrong-shaped objects, forgotten arguments - produce no error at all. They quietly yield `undefined` or `NaN`, which flows downstream and breaks something *far* from the actual bug.

Watch it happen:

```javascript runnable
function priceWithTax(item, rate) {
  return item.price + item.price * rate;
}

const product = { name: "Notebook", cost: 12 }; // oops: "cost", not "price"

console.log(priceWithTax(product, 0.2));
```
```console
NaN
```
*What just happened:* `product` has a `cost` field, but `priceWithTax` reads `item.price`. Reading a missing property isn't an error in JavaScript - it returns `undefined`, so `undefined + undefined * 0.2` becomes `NaN`, returned without a peep. The `NaN` lands in a cart total or a database row, and three screens later something looks wrong, with nothing pointing at the misspelled field. A type checker would have underlined `item.price` the instant you wrote it, because it knows `product` has no `price`.

⚠️ **The dangerous part isn't the crash - it's the *lack* of one.** A crash at least points at a line. A silent `undefined`/`NaN` travels far from its source before causing visible damage, which is why these bugs eat hours. Dynamic typing trades upfront freedom for this exact class of late, confusing failures.

## What static typing buys you

📝 **Static typing** - the types of your values are declared (or inferred) and checked *before* the program runs, usually right in your editor as you type. "Static" means "without running it": the check happens at rest, on the source code itself.

Flip the previous bug into a statically-typed world and it never reaches the browser:

- **Bugs caught in the editor.** The `item.price` typo gets a red underline immediately - fixed before you ever run the code.
- **Autocomplete that knows your shapes.** The editor knows `product` has `name` and `cost`, so typing `product.` offers exactly those two - no guessing field names or flipping back to the definition.
- **Types as living documentation.** A signature like `priceWithTax(item: Product, rate: number)` tells the next reader precisely what to pass - and unlike a comment, it can't drift out of date, because the checker enforces it.
- **Safer refactors.** Rename a field or change a function's arguments, and the checker flags *every* call site that no longer fits - a map instead of a flashlight.

💡 **The shift in feel.** Dynamic typing finds mistakes at runtime, scattered across a session. Static typing finds them at edit-time, all at once, before anything runs. The bugs were always there - static typing just moves discovery to the cheapest moment.

## TypeScript = JavaScript + a type layer

Getting static checking in a language that doesn't have it means adding a layer, not switching languages. That layer is **TypeScript**.

📝 **TypeScript** - a *superset* of JavaScript: every valid JavaScript program is already valid TypeScript. You optionally add type annotations on top, a checker verifies they hold together, and it compiles down to plain JavaScript that runs anywhere JS runs.

"Superset" is the key word: you don't rewrite code to adopt TypeScript, you rename a file and add types where they help. Here's the taxed-price function, annotated:

```typescript
interface Product {
  name: string;
  price: number;
}

function priceWithTax(item: Product, rate: number): number {
  return item.price + item.price * rate;
}

const product = { name: "Notebook", cost: 12 };
console.log(priceWithTax(product, 0.2)); // Error flagged here
```

*What just happened:* `interface Product` declares the shape an item must have: a `name` string and a `price` number. The function signature takes a `Product` and a `number`, returning a `number`. Passing `{ name, cost }`, the checker compares it against `Product`, sees there's no `price` (and a stray `cost`), and reports the error **in your editor, before you run anything** - something like *"Property 'price' is missing in type."* The exact bug from the runnable demo above, caught at rest. Once satisfied, TypeScript strips the annotations and emits plain JavaScript.

⚠️ **Types are erased at compile time - they don't exist at runtime.** (The most common misconception about TypeScript.) TypeScript checks your code, then deletes every annotation and produces plain JavaScript. So a type *cannot* validate data that arrives while the program runs - a JSON response from a server, user input, a value from `localStorage`. TypeScript trusts you when you say "this API returns a `Product`"; if the server lies, nothing stops the bad data. For external data you still need real runtime checks (a validation library, or hand-written guards) - types catch mistakes *you* make in code, not the outside world.

## A gentler on-ramp: JSDoc types

Not ready for a build step and a `tsconfig.json`? Get most of the benefit in plain `.js` files, zero tooling beyond your editor, using **JSDoc** comments.

📝 **JSDoc** - a structured comment format (`/** ... */`) that describes a function's parameters and return type. Modern editors (anything running the TypeScript language service, including VS Code out of the box) *read* these comments and type-check against them - in regular JavaScript, no compiler in the pipeline.

```javascript
// @ts-check
/**
 * @param {{ name: string, price: number }} item
 * @param {number} rate
 * @returns {number}
 */
function priceWithTax(item, rate) {
  return item.price + item.price * rate;
}

const product = { name: "Notebook", cost: 12 };
priceWithTax(product, 0.2); // editor underlines this - wrong shape
```

*What just happened:* The `@param` and `@returns` tags spell out the same types as the TypeScript version, but live in a comment inside an ordinary `.js` file. The `// @ts-check` line at the top is the switch that turns the error underlines on - autocomplete on `item.` works without it, but the red squiggle for the wrong-shaped `product` needs `// @ts-check` per file (or `checkJs` set once in a `jsconfig.json`). The file still runs as plain JavaScript - both the comment and the tags are invisible to the runtime, so no build, new file extension, or deploy changes.

💡 **Why this matters.** JSDoc is low-commitment: *feel* what typing does before committing to a toolchain. Many large codebases run entirely on JSDoc-typed JavaScript. If a build step feels like a big leap, start here: add types to one tricky module and watch the bugs surface.

## Where to go

You already understand the JavaScript underneath, the hard part - TypeScript is just that language plus a checker, so the leap is short.

The standout next step: [TypeScript from Zero](/guides/typescript-from-zero), picking up exactly where this leaves off - interfaces and unions, generics, narrowing, how to type real-world data safely, and how to wire the compiler into a real project.

## Recap

1. **Dynamic typing** checks types only while the program runs - so typos and wrong-shaped arguments often produce a silent `undefined`/`NaN` instead of an error, and the damage surfaces far from the cause.
2. **Static typing** checks types *before* anything runs: bugs caught in the editor, autocomplete that knows your shapes, types as self-enforcing documentation, and refactors that flag every broken call site.
3. **TypeScript is a superset of JavaScript** - valid JS is valid TS. You add annotations, a checker verifies them, and it compiles down to plain JavaScript that runs everywhere JS does.
4. ⚠️ **Types are erased at compile time** - they don't exist at runtime, so they can't validate external/network data on their own; you still need runtime checks for data from outside your code.
5. **JSDoc** gives you much of the checking in plain `.js` files with no build step - a low-commitment way to try typing in your editor today.
6. The deep next step is [TypeScript from Zero](/guides/typescript-from-zero): you already know the JavaScript, so the jump is mostly learning the type layer.

## Quick check

Lock in the core ideas - when bugs get caught, what "superset" means, and the one thing types *can't* do:

```quiz
[
  {
    "q": "Why did `priceWithTax(product, 0.2)` return `NaN` instead of throwing an error, when `product` had a `cost` field but the function read `item.price`?",
    "choices": [
      "Reading a missing property returns `undefined`, and arithmetic on `undefined` produces `NaN` - JavaScript never flags the typo at all",
      "JavaScript threw an error, but it was silently swallowed by the function",
      "The `0.2` argument was the wrong type, so the multiplication failed",
      "`NaN` is JavaScript's way of warning you about a misspelled property name"
    ],
    "answer": 0,
    "explain": "In dynamic typing, reading a property that doesn't exist yields `undefined` with no error. `undefined + undefined * 0.2` is `NaN`, returned silently - the misspelled field is never caught. A type checker would have flagged `item.price` at edit-time."
  },
  {
    "q": "What does it mean that TypeScript is a 'superset' of JavaScript?",
    "choices": [
      "Every valid JavaScript program is also valid TypeScript; TS adds an optional type layer on top",
      "TypeScript replaces JavaScript with entirely new syntax you must learn from scratch",
      "TypeScript runs in the browser directly, without compiling to JavaScript",
      "TypeScript is a faster runtime that executes JavaScript more efficiently"
    ],
    "answer": 0,
    "explain": "A superset contains everything the base has, plus more. Valid JS is already valid TS, so you adopt it incrementally by adding annotations. The checker verifies them, then TS compiles down to plain JavaScript that runs anywhere JS runs."
  },
  {
    "q": "Why can't TypeScript types, on their own, validate a JSON response coming back from a server at runtime?",
    "choices": [
      "Types are erased at compile time, so they don't exist while the program runs - you still need real runtime checks for external data",
      "TypeScript can validate server data, but only if you pay for the enterprise tier",
      "Server responses are always strings, which TypeScript refuses to type",
      "Types validate runtime data automatically, so no extra checks are ever needed"
    ],
    "answer": 0,
    "explain": "TypeScript checks your code, then strips all annotations and emits plain JavaScript. Since types don't exist at runtime, they can't police data arriving from outside - a server, user input, storage. You add runtime validation (a library or hand-written guards) for that."
  }
]
```


---

# Where to Go Next - Straight Signposts From Here

You made it. You can write JavaScript, reason about async, manipulate the DOM, handle errors, and read a real project's tooling without flinching - the foundation every JavaScript career is built on. Everything below is *application* of what you already know.

This phase isn't more syntax - it's a map: the straight paths from here, what each is *for*, and what to build to make it stick.

## The branches from here

```mermaid
flowchart TD
  You[You: solid JavaScript] --> FE[Frontend framework]
  You --> BE[Backend: Node + Express]
  You --> TS[TypeScript]
  FE --> FS[Full-stack]
  BE --> FS
```

*What this shows:* three directions lead out from where you stand, and they converge. You're not locked in forever, but pick *one to go deep on next* - depth beats breadth while learning.

## Frontend frameworks - building real UIs

You used `querySelector` and `addEventListener` to change the page by hand - fine for small things, but a tangle once an app has dozens of interacting pieces. **Frameworks** fix this: describe what the UI *should look like* for a given state, and the framework keeps it in sync.

- **React** - most widely used, safest bet for jobs, biggest ecosystem. Component-based; you'll meet "JSX" and "hooks."
- **Vue** - gentle learning curve, lovely docs, approachable after plain JS.
- **Svelte** - compiles components away, so less framework at runtime; many find it the most pleasant to write.

> 📝 They're more alike than internet arguments suggest - all three are component-based, state-drives-the-UI. Learn *one* well; the concepts transfer.

For employability, **React** is the pragmatic choice. For joy, try **Svelte** or **Vue**.

## Backend - JavaScript on the server

The same language you've been writing runs servers, thanks to **Node** (Phase 8). The classic starting point is **Express** - a small framework for web servers and APIs: code that listens for requests, talks to a database, and sends back JSON, the other end of the `fetch` calls you already know.

The natural next step if you liked `fs` and `fetch` more than the DOM - building an API your frontend talks to is one of programming's most satisfying moments.

> 💡 If "request," "response," "status code," and "JSON" still feel fuzzy, spend an hour with [HTTP and JSON API Basics](/guides/http-and-json-api-basics) before diving into Express.

## TypeScript - typed JavaScript, and yes, learn it

**TypeScript** is JavaScript with a type system bolted on: annotate what variables and functions expect (`name: string`, `age: number`), and a checker catches whole categories of bugs *before you run the code* - the "undefined is not a function," "forgot an await" mistakes from this course, flagged in your editor as you type.

It compiles to plain JavaScript, runs everywhere JavaScript runs, and nearly every serious codebase uses it now.

**Learning TypeScript next is strongly worth it.** It's not a different language - it's the JavaScript you know plus a safety net. Fewer bugs, better autocomplete, and a short leap *because* you already understand the JavaScript underneath.

> 💡 Don't learn TypeScript *first* and JavaScript *never* - you'd be fighting types without understanding the language beneath them. You did this in the right order.

## Full-stack - the whole picture

Combine a frontend framework with a Node backend and a database, and you're **full-stack** - building complete applications end to end. Tools like Next.js (React) or SvelteKit (Svelte) blur the frontend/backend line and let one project do both. Where the branches converge - realistic within months, not years.

## What to actually build

Reading guides got you here; *building* turns knowledge into skill. Aim small enough to finish but real enough to teach the messy parts:

1. **A quiz or to-do app, plain JS + DOM.** No framework. Cements Phases 6–9 - events, state, async.
2. **A page that fetches a public API and displays it.** Weather, GitHub repos, anything with a free JSON API. Practices `fetch`, error handling, and the DOM together.
3. **The same app, rebuilt in a framework.** Now you'll *feel* what React/Vue/Svelte do, since you remember doing it by hand.
4. **A tiny Express API plus a frontend that talks to it.** Your first full-stack thing - the moment both halves connect is when it clicks.

Finish each one - a finished rough project teaches more than three polished ones abandoned at 80%.

## A last word

If how programming languages relate still feels hazy - why JavaScript made its choices, how it compares to Python or Rust - [Languages, Explained Like a Human](/guides/languages-explained-like-a-human) puts it in context.

You started this course unsure what `npm run dev` even did. Now you read real code, reason about async, and choose your next step on purpose, not by panic. Go build the small thing; the rest is more of what you already know.

## Recap

1. **Pick one direction to go deep:** **frontend framework** (React for jobs, Svelte/Vue for joy), **backend** (Node + Express), or **TypeScript**.
2. **TypeScript is the standout next step** - typed JavaScript that catches bugs early; short leap since you know the JS underneath.
3. **Full-stack** (frontend + Node backend + database) is where the paths converge - realistic in months.
4. **Build to learn:** plain-JS app → API-fetching page → rebuild in a framework → tiny full-stack app. *Finish each one.*
