JS::DOCS
⌂
JS
JavaScript Documentation

The Complete JavaScript Guide

Basic to Advanced — every core language concept explained in detail, with a live, editable code example under each one.

36 topics 82 live examples 5 sections
Documented By MD Eesha Sr. Software Engineer
Complete Language Reference

JavaScript, Basic to Advanced

A hands-on JavaScript reference covering every core concept from your first variable to async programming and the event loop — with a live editor under every example so you can run, break, and rewrite the code right here.

▶How to use this doc: every code block below is editable. Change anything you like and press Run to execute it and see the output in the console panel underneath. Press Reset to restore the original example.
Getting Started

What Is JavaScript?

JavaScript is the programming language of the web. Every button click, form validation, animation, and live update you see in a browser is driven by it — and thanks to Node.js, the same language now runs servers, command-line tools, and even mobile apps. It's dynamically typed (you don't declare a variable's type up front), interpreted (no separate compile step), and single-threaded with a clever concurrency model called the event loop that lets it stay responsive while waiting on slow things like network requests.

Where does JavaScript run?

Three places you'll use constantly: inside a <script> tag in an HTML page, directly in your browser's DevTools console (right-click → Inspect → Console), or on a server via Node.js by running node file.js in a terminal. This document uses a fourth option: a live sandbox, so you never have to leave the page.

Your first line of JavaScript

The function you'll use more than any other while learning is console.log() — it prints a value so you can see what your code is doing. Try editing the message below and pressing Run.

Tip Every playground in this document auto-runs once so you can see the expected output immediately. Edit the code and press Run as many times as you like — nothing you do here affects anything outside this page.
📄 Official Docs — Introduction on MDN ↗
01 · Fundamentals

Variables

A variable is a named container for a value you want to store and reuse. JavaScript gives you three keywords to create one — let, const, and the older var — and the difference between them is almost entirely about scope, reassignment, and hoisting behavior.

var vs. let vs. const — quick comparison

Featurevarletconst
ScopeFunction or GlobalBlock ({ })Block ({ })
ReassignmentAllowedAllowedForbidden
RedeclarationAllowed (same scope)Forbidden (same scope)Forbidden (same scope)
Hoisting behaviorHoisted, initialized as undefinedHoisted, but unusable until declared (TDZ)Hoisted, but unusable until declared (TDZ)
Initial value requiredNoNoYes
TDZ? Short for "Temporal Dead Zone" — the gap between when a let/const variable is hoisted and when its declaration line actually runs. Accessing it during that gap throws a ReferenceError instead of silently returning undefined. See Scope & Hoisting for a runnable example.

let — for values that will change

Use let when you know the variable's value needs to be updated later, like a counter or a running total. It's block-scoped, meaning it only exists inside the { } it was declared in.

const — for values that won't be reassigned

Use const by default for everything else. It prevents the variable itself from being reassigned — though if it holds an object or array, the contents can still be modified; only the reference is locked.

var — the legacy keyword

You'll still see var in older code. It's function-scoped instead of block-scoped (so it "leaks" out of if blocks and loops) and can be redeclared, which causes subtle bugs. Modern JavaScript style guides recommend avoiding it entirely in favor of let and const.

Naming rules Variable names must start with a letter, $, or _, can't start with a digit, and are case-sensitive (age and Age are different variables). By convention, JavaScript uses camelCase for variables and functions.
📄 Official Docs — Variables on MDN ↗
02 · Fundamentals

Data Types

Every value in JavaScript is one of two kinds: a primitive (immutable, copied by value) or an object (mutable, copied by reference). Understanding which is which explains a lot of behavior that otherwise looks like magic.

TypeCategoryExampleNotes
StringPrimitive"hello"Text, wrapped in quotes or backticks
NumberPrimitive42, 3.14Both integers and decimals — one type for both
BigIntPrimitive42nFor integers larger than Number can safely hold
BooleanPrimitivetrue, falseLogical yes/no
undefinedPrimitiveundefinedA variable that's been declared but not assigned
nullPrimitivenullAn intentional "no value", set explicitly
SymbolPrimitiveSymbol("id")A guaranteed-unique value, often used as an object key
ObjectReference{ name: "Ava" }Key-value collections — arrays and functions are objects too

Checking a type with typeof

The typeof operator tells you what kind of value you're dealing with — essential for debugging and validating input.

Primitives vs. objects: copy by value vs. reference

This is one of the most important mental models in the language. Copying a primitive copies its value; copying an object copies a reference to the same underlying data.

Type conversion

JavaScript will often convert types for you automatically ("coercion"), which is convenient but a common source of bugs. It's usually safer to convert explicitly.

📄 Official Docs — Data Types on MDN ↗
03 · Fundamentals

Operators

Operators combine and compare values. Beyond the arithmetic you already know, JavaScript has a few operators worth learning deliberately because they show up constantly in real code.

OperatorCategoryExampleResult
+ - * / %Arithmetic7 % 21 (remainder)
** Exponent2 ** 8256
=, +=, -=Assignmentx += 5same as x = x + 5
=== / !==Strict equality1 === "1"false
&& / || / !Logicaltrue && falsefalse
?:Ternaryage >= 18 ? "adult" : "minor"inline if/else
??Nullish coalescingvalue ?? "default"fallback only for null/undefined
?.Optional chaininguser?.address?.cityundefined instead of a crash
?? vs || || falls back whenever the left side is any "falsy" value (0, "", null, undefined, NaN). ?? only falls back for null or undefined — use it whenever 0 or "" should count as a valid value.
📄 Official Docs — Operators on MDN ↗
04 · Fundamentals

Control Flow

Control flow statements decide which code runs, based on a condition. if/else handles most cases; switch is a cleaner alternative when you're comparing one value against many possibilities.

if / else if / else

if-else-flow.svg
describeTemp(celsius) celsius <= 0? Yes return "freezing" No celsius < 15? Yes return "cold" No celsius < 25? Yes return "mild" No (else) return "hot"

Each diamond is one condition, checked top to bottom. The moment a condition is true, its Yes branch fires and the function returns immediately — the rest of the chain is never checked. Only if every condition is false do you fall through to the final else.

switch

Each case falls through to the next unless you break — a common source of bugs, so don't forget it.

📄 Official Docs — Control Flow on MDN ↗
05 · Fundamentals

Loops

Loops repeat a block of code. Pick the loop that matches your data: a counted for loop for numeric ranges, for...of for iterating values in arrays/strings, and while when you don't know the number of iterations ahead of time.

for

loop-cycle.svg
let i = 1 1 i <= 5? 2 Yes console.log("count:", i) 3 i++ 4 repeat No Exit loop

Every loop shares this same shape: ① set up a starting value once, ② check the condition, ③ run the body while it's true, ④ update the counter, then jump back to ② and check again. The loop only ends once the condition finally comes back false.

while & do...while

while checks the condition before each pass; do...while always runs at least once because it checks after.

for...of and for...in

for...of loops over the values of an iterable (arrays, strings, Maps, Sets). for...in loops over the keys of an object — avoid using it on arrays.

break and continue

📄 Official Docs — Loops on MDN ↗
06 · Fundamentals

Functions

A function is a reusable block of code you define once and call as many times as you need. JavaScript gives you three main ways to write one, plus a rich set of parameter features.

Function declarations vs. expressions vs. arrow functions

A declaration is hoisted (usable before its definition in the file). An expression assigns a function to a variable and is not hoisted. An arrow function is a shorter expression syntax that also doesn't bind its own this (see The "this" Keyword).

function-call-return.svg
Caller YOUR CODE add(2, 3) sum = 5 1 4 function add(a, b) PARAMETERS RECEIVED a = 2, b = 3 return a + b; evaluates to 5 2 3 call: 2 → a, 3 → b return: 5 travels back

① Calling add(2, 3) sends its arguments across into the function's parameters — a and b. ②③ The function body runs using only those parameters and computes a + b. ④ The result travels back out through return and becomes the value of the call itself — that's why sum = add(2, 3) ends up holding 5.

Default and rest parameters

Default parameters fill in a value when the caller doesn't provide one. Rest parameters collect any number of extra arguments into a real array.

Every function returns something

If there's no explicit return, a function returns undefined. This trips up beginners more than almost anything else.

When to use which Reach for a declaration for top-level, named functions you want available anywhere in the file. Use an arrow function for short callbacks (passed to map, event handlers, etc.) and anywhere you need to preserve the surrounding this.
📄 Official Docs — Functions on MDN ↗
07 · Core Data Structures

Strings & String Methods

Strings represent text and are immutable — every "modifying" method actually returns a brand-new string rather than changing the original.

MethodDescription
.lengthNumber of characters (a property, not a method)
.toUpperCase() / .toLowerCase()Change case
.trim()Remove whitespace from both ends
.slice(start, end)Extract a section; accepts negative indexes
.split(separator)Break a string into an array
.includes(text)Check whether a substring exists
.replace(old, new)Replace the first match (use a /g regex for all)
.indexOf(text)Position of the first match, or -1
.padStart(len, char)Pad the start until it reaches a length
Template literals for building strings Instead of chaining +, use backtick strings with ${expression} — see the Template Literals section.
📄 Official Docs — Strings on MDN ↗
08 · Core Data Structures

Numbers & Math

JavaScript has a single Number type for both integers and decimals, built on 64-bit floating point — which is why decimal math can look slightly "off," and why the built-in Math object exists for everything beyond basic arithmetic.

📄 Official Docs — Numbers & Math on MDN ↗
09 · Core Data Structures

Arrays

An array is an ordered list of values, indexed from zero. Arrays are objects under the hood, which is why typeof [] reports "object" — but they come with a huge set of purpose-built methods, covered next.

📄 Official Docs — Arrays on MDN ↗
10 · Core Data Structures

Array Methods

These higher-order methods are how array data is actually processed in modern JavaScript — they replace most manual for loops over arrays and read more like a description of intent than a mechanical procedure.

MethodWhat it does
.map()Transforms every item, returns a new array of the same length
.filter()Keeps only items that pass a test, returns a shorter (or equal) array
.reduce()Folds the whole array down into a single value
.forEach()Runs a function per item; returns nothing (for side effects only)
.find() / .findIndex()Returns the first matching item, or its index
.some() / .every()Returns true if any / all items pass a test
.sort()Sorts in place — always pass a compare function for numbers
.includes()True if the array contains a given value
.slice() / .splice()slice copies a range; splice mutates the array in place
.concat() / spreadCombine arrays without mutating either one
.flat()Flattens nested arrays by a given depth
.join()Combines items into a single string with a separator
.push() / .pop() / .shift() / .unshift()Add or remove items from either end — all mutate in place
.indexOf() / .lastIndexOf()Index of the first (or last) matching value, or -1
.at()Index from the end with a negative number — no length math
.findLast() / .findLastIndex()Like find/findIndex, but searches from the end
.flatMap()map() then flatten one level, in a single pass
.reduceRight()Like reduce, but walks the array right to left
.reverse() / .fill() / .copyWithin()Mutate the array's existing slots in place
.entries() / .keys() / .values()Iterators over [index, value] pairs, indexes, or values
.toString() / .toLocaleString()Convert to a comma-joined, or locale-formatted, string
.toReversed() / .toSorted() / .toSpliced() / .with()Non-mutating copies of reverse/sort/splice/index-assign
Array.from() / Array.of() / Array.isArray()Build or check arrays — called on Array itself, not an instance

map — transform every item

filter — keep only what matches

reduce — fold into one value

reduce is the most flexible (and most confusing at first) array method: it walks the array carrying an "accumulator" forward.

forEach vs. map

forEach is for side effects (like logging); it always returns undefined. If you need a new array back, use map instead.

find, some, every

sort — always pass a compare function for numbers

slice, splice, concat, flat, join

push, pop, shift, unshift — add & remove from either end

All four mutate the original array and all four run in constant time at the end, but shift/unshift have to re-index every remaining item, so they're slower on large arrays than push/pop.

indexOf, lastIndexOf, includes — find a value

at() — index from the end, no length math

findLast, findLastIndex — search from the end

flatMap — map, then flatten one level

reduceRight — like reduce, but right to left

reverse, fill, copyWithin — mutate existing slots in place

entries, keys, values — iterate with more context

toString, toLocaleString

Non-mutating copies (ES2023) — toReversed, toSorted, toSpliced, with

These are the "safe" counterparts to reverse/sort/splice — same result, but they return a new array and leave the original completely untouched.

Array static methods — Array.from, Array.fromAsync, Array.isArray, Array.of

These are called on Array itself, not on an existing array instance.

📄 Official Docs — Array Methods on MDN ↗
11 · Core Data Structures

Objects & Object Methods

An object stores data as key-value pairs. It's the closest thing JavaScript has to a general-purpose "record" — most real-world data (a user, a product, an API response) ends up modeled as one. Beyond reading and writing properties, the Object constructor itself ships a full set of static methods for merging, locking down, and inspecting objects.

MethodWhat it does
Object.keys() / .values() / .entries()Arrays of an object's own keys, values, or [key, value] pairs
Object.fromEntries()The reverse of .entries() — builds an object back from pairs
Object.assign()Shallow-copies properties from one or more sources into a target
Object.create()Creates a new object with a given prototype
Object.freeze() / .seal() / .preventExtensions()Lock an object down, with increasing strictness
Object.isFrozen() / .isSealed() / .isExtensible()Check which lock (if any) is currently applied
Object.defineProperty() / .defineProperties()Add a property with fine-grained control (getters, enumerability…)
Object.getOwnPropertyDescriptor(s)()Read that fine-grained configuration back out
Object.getPrototypeOf() / .setPrototypeOf()Read or change what an object inherits from
Object.getOwnPropertyNames() / .getOwnPropertySymbols()Own string keys, or own Symbol keys (keys() skips both non-enumerable and Symbol keys)
Object.is()Like === , but correctly handles NaN and -0/+0
Object.hasOwn()True if a property exists directly on the object (not inherited)
Object.groupBy()Groups an array's items into an object, keyed by a callback's result

Creating & accessing objects

Equality on objects {} === {} is always false — two different objects are never equal, even with identical contents, because === compares references, not structure. Only comparing an object to itself (the same reference) returns true.

Object.fromEntries — the reverse of Object.entries

Object.assign vs. spread — merging & copying

Object.create — build from a specific prototype

freeze, seal, preventExtensions — locking objects down

Three levels, from strictest to loosest: freeze blocks every change, seal allows editing existing properties but not adding/removing, preventExtensions only blocks adding new ones.

defineProperty & property descriptors

Every property has hidden configuration — writable, enumerable, configurable — that plain assignment sets to true by default. defineProperty lets you set it explicitly, or attach a getter.

Prototype & key introspection

Object.is, Object.hasOwn, Object.groupBy — modern helpers

📄 Official Docs — Objects on MDN ↗
12 · Core Data Structures

Destructuring

Destructuring unpacks values from arrays or objects into standalone variables in one line — instead of accessing each property individually.

Object destructuring

Array destructuring

📄 Official Docs — Destructuring on MDN ↗
13 · Core Data Structures

Spread & Rest Operators

Both use the same ... syntax but do opposite jobs: spread expands a collection out into individual elements; rest gathers individual elements back into a collection. Which one you're looking at depends on where it appears.

Spread — expand a collection

Rest — gather into a collection

📄 Official Docs — Spread & Rest on MDN ↗
14 · Core Data Structures

Template Literals

Backtick-delimited strings let you embed expressions directly with ${ }, and span multiple lines without escape characters — a big readability upgrade over string concatenation with +.

📄 Official Docs — Template Literals on MDN ↗
15 · Intermediate

Execution Context & the Call Stack

Before JavaScript runs a single line of your code, it builds a container called an execution context — one for the top-level (global) code, and a brand new one every time a function is called. Every execution context, no matter which one, is built in exactly the same two phases — and understanding those two phases is what makes hoisting, this, and stack overflows stop feeling like magic.

Phase 1: Memory Creation, Phase 2: Code Execution

JavaScript never just runs your file top to bottom in one pass. For every execution context it creates, it does two full passes over that context's code:

① Memory Creation Phase — JS scans the code without running any of it and sets up memory in advance: every var is registered and initialized to undefined, every function declaration is stored in full (the whole function body, not just its name), and this gets bound. This scan is exactly what "hoisting" is — it's not variables physically moving to the top of the file, it's JavaScript pre-allocating memory for them before execution starts.

② Code Execution Phase — only now does JavaScript run your code line by line, top to bottom, for real: variables are assigned their actual values (overwriting the undefined placeholder from Phase 1), expressions are evaluated, and every function call pushes a brand new execution context — with its own Phase 1 and Phase 2 — onto the call stack.

execution-context.svg
Execution Context BUILT FOR GLOBAL CODE & EVERY FUNCTION CALL ① MEMORY CREATION PHASE var message → undefined function greet → stored in full this → bound ② CODE EXECUTION PHASE message = "hi" → real value assigned greet() → a NEW execution context is created console.log(...) → actually runs Call Stack LIFO · LAST IN, FIRST OUT greet() Execution Context runs its own Phase ① then ② 2 Global Execution Context created first, before any code runs 1 pops off when greet() returns Every stack frame is really this same 2-phase box — global code included.

The left box is the anatomy of a single execution context — Phase ① always finishes completely before Phase ② begins. The right box is the call stack: the Global Execution Context goes in first, and calling greet() pushes a second execution context on top of it — one that runs through the exact same two phases on its own code. When greet() returns, its context is popped off and control drops back to the one underneath.

Why it matters Hoisting isn't a special rule to memorize — it's just a visible side effect of Phase 1. And a "Maximum call stack size exceeded" error (usually from runaway recursion) is exactly what it sounds like: too many execution contexts pushed on top of each other with nothing popping them back off. See Scope & Hoisting next for how let/const behave differently in Phase 1 via the Temporal Dead Zone.
📄 Official Docs — Execution Context on MDN ↗
16 · Intermediate

Scope & Hoisting

Scope determines where a variable is visible. Hoisting describes how JavaScript sets up variables and functions before running your code line-by-line — which explains some surprising ordering behavior.

Block scope vs. function scope

Hoisting & the Temporal Dead Zone

Function declarations are hoisted completely (you can call them before they appear in the file). var declarations are hoisted but start as undefined. let and const are hoisted too, but stay in a "Temporal Dead Zone" — unusable until their line actually runs — which is why they throw instead of silently returning undefined.

📄 Official Docs — Scope & Hoisting on MDN ↗
17 · Intermediate

Closures

A closure is a function that "remembers" the variables from the scope it was created in, even after that outer function has finished running. It's how JavaScript implements private state without classes — and it powers patterns like counters, memoization, and event handler factories.

A practical use: function factories

Why it matters Closures are what let a variable stay "alive" and private after its enclosing function returns. Every time you write a callback that references an outer variable, you're using a closure — it's one of the most-asked-about interview topics for a reason.
📄 Official Docs — Closures on MDN ↗
18 · Intermediate

The "this" Keyword

this refers to "whatever object is calling the function" — and unlike most languages, that's determined by how a function is called, not where it's defined. This is the single most common source of JavaScript bugs for people coming from other languages.

Rule of thumb Use a regular function for object methods (so this refers to the object). Use an arrow function for callbacks nested inside a method, so this correctly stays pointed at the outer object instead of becoming undefined.
📄 Official Docs — The "this" Keyword on MDN ↗
19 · Intermediate

Higher-Order Functions & Callbacks

In JavaScript, functions are values — they can be stored in variables, passed as arguments, and returned from other functions. A higher-order function is any function that does at least one of those things. A callback is simply a function passed in to be called later.

📄 Official Docs — Higher-Order Functions on MDN ↗
20 · Intermediate

Error Handling

try/catch lets your program recover from a runtime error instead of crashing entirely. Use it around code that can realistically fail — parsing data, calling an API, reading user input — not around every single line.

Custom error types

📄 Official Docs — Error Handling on MDN ↗
21 · Intermediate

JSON

JSON (JavaScript Object Notation) is the universal text format for exchanging data between a browser and a server. It looks almost identical to a JS object literal, but it's a separate format with stricter rules (keys must be double-quoted strings, no functions or undefined allowed).

📄 Official Docs — JSON on MDN ↗
22 · Intermediate

Date Object

The built-in Date object handles dates and times. Its API predates modern conventions in a few places (months are zero-indexed!), which is worth knowing before it surprises you.

Heads up For anything beyond basic date math (timezones, formatting, durations), most real projects reach for a library like date-fns or Intl.DateTimeFormat rather than hand-rolling it.
📄 Official Docs — Date Object on MDN ↗
23 · Intermediate

Regular Expressions

A regular expression (regex) is a pattern for matching text — used for validation (is this a valid email?), searching, and find-and-replace operations that are too flexible for plain string methods.

PatternMatches
\dAny digit
\wAny word character (letter, digit, underscore)
\sAny whitespace
+ * ?One-or-more / zero-or-more / zero-or-one
^ $Start / end of the string
g flagMatch globally, not just the first occurrence
📄 Official Docs — Regular Expressions on MDN ↗
24 · Intermediate

Map & Set

Two collection types introduced in ES6 that solve problems plain objects and arrays handle awkwardly: Map is a key-value store that allows any value as a key, and Set is a list that automatically enforces uniqueness.

Map — key-value pairs, any key type

Set — a list of unique values

📄 Official Docs — Map & Set on MDN ↗
25 · Browser & DOM

DOM Manipulation

The DOM (Document Object Model) is the browser's live, in-memory representation of your HTML page as a tree of objects. JavaScript's DOM API lets you read and change that tree — which is how a page updates without a full reload. These examples run in a real mini-page below the editor, not just the console.

Selecting elements

Creating and changing elements

Attributes vs. properties

📄 Official Docs — DOM Manipulation on MDN ↗
26 · Browser & DOM

Events

Events are how a page responds to what a user does — clicks, typing, scrolling, submitting a form. addEventListener is the standard way to react to them: it attaches a callback that runs whenever the event fires.

Listening for a click

The event object

The callback receives an event object describing what happened — which key was pressed, where the mouse was, which element was the target, and so on.

Event delegation

Instead of attaching a listener to every single button (expensive, and misses buttons added later), attach one listener to a shared parent and check event.target.

📄 Official Docs — Events on MDN ↗
27 · Browser & DOM

Local & Session Storage

Both let you save key-value data directly in the browser, no server needed. localStorage persists until explicitly cleared (even after closing the tab); sessionStorage disappears when the tab closes. Both only store strings — objects must be serialized with JSON.stringify.

📄 Official Docs — Local & Session Storage on MDN ↗
28 · Advanced & Async

Prototypes & Inheritance

Every JavaScript object has a hidden link to another object called its prototype, which it can borrow properties and methods from. This "prototype chain" is how inheritance worked in JavaScript long before the class keyword existed — and classes (next section) are really just a friendlier syntax built on top of it.

Why this matters When you call a method on an object and JavaScript can't find it directly on that object, it walks up the prototype chain looking for it. This is also how built-ins work — every array inherits .map(), .push(), etc. from Array.prototype.
📄 Official Docs — Prototypes & Inheritance on MDN ↗
29 · Advanced & Async

Classes

ES6 classes are syntax sugar over prototypal inheritance — a cleaner, more familiar way (especially if you know other languages) to define a blueprint for creating objects with shared behavior.

Getters, setters, and static members

📄 Official Docs — Classes on MDN ↗
30 · Advanced & Async

Promises

A Promise represents a value that isn't ready yet but will be — the result of something asynchronous like a network request or a timer. It's always in one of three states: pending, fulfilled, or rejected, and it replaced the deeply-nested "callback hell" pattern from early JavaScript.

Running promises in parallel

.all vs .race vs .allSettled Promise.all waits for every promise and rejects immediately if any one fails. Promise.race settles as soon as the first one finishes, win or lose. Promise.allSettled waits for all of them and never short-circuits, giving you a status report for each.
📄 Official Docs — Promises on MDN ↗
31 · Advanced & Async

Async / Await

async/await is syntax that lets promise-based code read top-to-bottom like synchronous code, instead of a chain of .then() calls. Under the hood it's still promises — this is just a more readable way to write them.

Error handling with try/catch

📄 Official Docs — Async / Await on MDN ↗
32 · Advanced & Async

Fetch API

fetch() is the standard browser function for making HTTP requests — calling an API, loading data, submitting a form without a page reload. It returns a Promise that resolves to a Response object, which you then read with .json() (also a Promise, since parsing the body is async).

About this playground This sandboxed page has no real network access, so the examples below use mockFetch — a stand-in with the exact same shape as the real thing. In your own project, replace it with the real fetch(url) and the rest of the code is identical.

fetch with async/await

📄 Official Docs — Fetch API on MDN ↗
33 · Advanced & Async

The Event Loop

JavaScript runs on a single thread, so it can't truly do two things at once — yet it handles timers, network requests, and UI events without freezing. The event loop is the mechanism that makes this possible: it constantly checks whether the call stack is empty, and if so, pulls the next queued callback in to run.

event-loop.svg
Call Stack LIFO · SINGLE THREAD console.log(...) — sync main() 1 2 Web APIs BROWSER / NODE setTimeout(fn, 0) Microtask Queue PROMISE CALLBACKS Promise.then callback 3 Macrotask Queue TIMERS · I/O · UI EVENTS setTimeout callback 4 EVENT LOOP stack empty? setTimeout() call registers a timer timer elapses, callback queued Promise.then() queued directly drained first, every tick then one macrotask, only if empty pushes next callback onto stack

Reading the diagram against the example above: ①② the two synchronous console.log calls run directly on the call stack. ③ Promise.then() schedules its callback straight onto the microtask queue. ④ setTimeout hands off to a Web API, which only pushes the callback onto the macrotask queue once the timer elapses. Once the call stack is empty, the event loop drains the entire microtask queue first, and only then pulls a single macrotask — which is why the output lands in the order 1, 2, 3, 4.

Why the order looks strange All synchronous code runs first, in order, to completion. Then, before the next "macrotask" (like a setTimeout callback) is allowed to run, the entire microtask queue (Promise callbacks) is fully drained. That's why a Promise.then always jumps ahead of a setTimeout(fn, 0), no matter the order they were written in.
📄 Official Docs — The Event Loop on MDN ↗
34 · Advanced & Async

Modules

Modules let you split code across multiple files and explicitly choose what each file shares (export) and pulls in from elsewhere (import), instead of everything living in one giant global scope. Because modules only work across real files loaded by a browser or Node (not inside a single embedded sandbox like this document), the example below is a reference, not a live playground.

math.js
// Named exports — a file can have as many as it needs
export function add(a, b) {
  return a + b;
}
export const PI = 3.14159;

// Default export — a file can only have one
export default function multiply(a, b) {
  return a * b;
}
app.js
import multiply, { add, PI } from "./math.js";

console.log(add(2, 3));       // 5
console.log(multiply(2, 3));  // 6
console.log(PI);              // 3.14159
In the browser Load a module with <script type="module" src="app.js"></script>. In Node.js, either use this same import/export syntax in a .mjs file (or a project with "type": "module" in package.json), or use the older CommonJS require()/module.exports style still common in existing codebases.
📄 Official Docs — Modules on MDN ↗
35 · Advanced & Async

Generators & Iterators

A generator is a special function that can pause itself with yield and resume later, producing a sequence of values on demand instead of computing them all up front. They're the mechanism for...of loops rely on under the hood, and they're handy for lazy sequences and custom iteration.

An infinite sequence, consumed lazily

📄 Official Docs — Generators & Iterators on MDN ↗
Reference

Official Documentation — All 36 Topics

Every topic in this guide, linked straight to its official reference on MDN (or the relevant spec) — for when you need more depth than a live example can give you.