Skip to the notes
JSGroundwork
JSGroundwork handwritten · web dev
✎Playground→⌘Problems↻Review🔥Progress

Chapters

27 chapters
⌕
Beginner9›
B1Setup & mental modelB2Types & valuesB3Operators & flowB4Functions (first half)B5Objects & arrays (first half)B6DOM & eventsB7Basic asyncB8Errors & tools★Cheat page
Intermediate10›
I1Scope & functions, properlyI2Objects deeplyI3Prototypes & OOPI4Async, properlyI5Modules & toolingI6Regex, dates & APIsI7Error handlingI8Real-time connectionsI9Offline & storage★Cheat page
Advanced10›
A1Engine & memoryA2Advanced asyncA3MetaprogrammingA4Types & dataA5Patterns & architectureA6PerformanceA7SecurityA8EcosystemA9Testing★Cheat page
/ search[ ] chaptert top

JavaScript levels

1Beginner2Intermediate3Advanced

Ready to read

JSJavaScript⑂Git◎Interview prepΣDSA in JSSDSystem Design
More topics15›
</>HTML{ }CSS⚛ReactNNext.jsNeNest.jsTSTypeScriptNoNode.js🐳DockerDBSQL & Databases✓Testing🔒Web Security☁Cloud & DevOps◈GraphQL◆Redis☸Kubernetes
100%
B1

Setup & mental model

The layer everybody skips — and it shows in interviews.

Two boxes, not one

Beginners think "JavaScript" is one thing. It's two things stacked, and almost every "weird JS behaviour" question traces back to which box actually did the work.

THE RUNTIME (Chrome / Node) THE ENGINE (V8) variables · functions · objects the call stack promises · garbage collector it can add 2 + 2. that's it. setTimeout fetch document / the DOM localStorage console.log the event loop fs (Node) process timers Everything in red is NOT part of the language. The browser (or Node) hands it to you.
Engine = the cook. Runtime = the whole restaurant (doors, waiters, clock).
Say it like this → "V8 has no idea what a timer is. The browser does. The engine runs the language, the runtime provides the world around it."

The 5 words that describe JS

  • Single-threaded — one worker. It can only do one thing at a time. A slow loop freezes your whole page.
  • Synchronous by default — line 1, then line 2, then line 3. Waiting doesn't happen unless you hand work to the runtime.
  • Dynamically typed — a variable is a box, not a shape. Any value can go in it, and what's in it can change.
  • Weakly typed — if types don't match, JS converts instead of complaining. "5" * 2 → 10.
  • Interpreted + JIT compiled — it starts running instantly, then quietly re-compiles the parts you run a lot into fast machine code.

"Dynamic" and "weak" are often confused, but they're separate questions. Dynamic asks when a type is checked — JS decides at runtime, not ahead of time. Weak asks what happens on a mismatch — JS converts instead of refusing. Python is dynamic but strongly typed: 1 + "1" is an error there. JS does both at once, which is why it gets blamed for more than its share.

Watch the single thread work

This is the whole model in one demo: one call stack, a queue for promises, and a separate queue for everything the runtime hands back (timers, clicks, I/O). Step through it — the code on the left is really the code running underneath the panel on the right.

Event loop, one step at a time
Call stack
Microtask queue — promises
Macrotask queue — timers
Console

The punchline: C prints before B, even though the timer's delay was 0ms. The microtask queue always drains completely before the event loop looks at a single macrotask. That's not a quirk of timers — it's the ordering rule every Promise, every async/await, and every render sits on top of.

Where your code actually runs

Three places you'll type JavaScript, and they behave differently.

  • The console — a REPL. It prints the result of every expression, which is why a stray undefined shows up after console.log(...) — that's log's own return value being echoed, not a bug.
  • A <script> tag — by default, HTML parsing stops while the script downloads and runs. That's why a plain <script> sitting in <head> delays everything below it from painting.
  • Node — node app.js runs a file; bare node opens a REPL. This is where JS gets a filesystem, a process, and no DOM at all.
⚠ file:// is not a server Double-clicking index.html works fine — until you add type="module", at which point every import silently fails. Modules need a real origin. Run one locally: npx serve or a "Live Server" extension.

var / let / const

var 💀 let const ⭐
Lives inside the whole function the nearest { } the nearest { }
Use before declaring undefined 💥 error 💥 error
Reassign yes yes no
Becomes a window property? yes, at the top level no no

Hoisting means: before running a line of code, JS scans the scope and registers every name in it. var names get created and pre-filled with undefined. let and const names get created but left empty — touch one early and you get an error. That empty gap has a dramatic name: the Temporal Dead Zone.

console.log(a);  // what happens?
console.log(b);  // what happens?
var a = 1;
let b = 2;

Run it above — the first line quietly prints undefined because var was pre-filled. The second line never gets the chance: b is still in the Temporal Dead Zone, so the whole script throws a real ReferenceError right there.

Rule const by default → let when you must reassign → var never.

Naming & comments

Identifiers may contain letters, digits, $ and _. They can't start with a digit and can't be a reserved word. They're case-sensitive — Name and name are two different variables.

const userName = "ana";        // camelCase — variables, functions
class UserAccount {}           // PascalCase — classes, constructors
const MAX_RETRIES = 3;         // UPPER_SNAKE — true fixed constants
const _internal = {};          // leading _ — "private" by convention only
class A { #secret = 1; }       // # — actually private (ES2022)

Name for intent, not type. Booleans read as questions (isActive, hasPermission), functions start with a verb (getUserById, handleSubmit).

// single line

/* multi
   line — these do NOT nest */

/**
 * JSDoc — tooling reads this for autocomplete and type hints.
 * @param {string} name
 */
function greet(name) { return "Hi " + name; }

Write comments that explain why, not what — the code already says what.

'use strict'

Added in ES5 to fix old design mistakes without breaking the existing web. It doesn't add powers — it removes the silence. Mistakes that used to fail quietly now throw a real error.

'use strict';
x = 5;   // no var, no let — what happens?

Run it — strict mode refuses to guess and throws ReferenceError: x is not defined. Delete the first line (or run it in a plain script) and the exact same assignment succeeds silently, quietly creating a global variable named x. That silent global is the bug strict mode exists to kill.

You rarely type 'use strict' yourself anymore, because modules and classes are strict automatically.

Script vs module

A genuine fork in how a file is parsed and run, decided before a single line executes.

<script src="a.js"></script>                 // classic script
<script type="module" src="a.js"></script>   // ES module
Classic script ES module
Strict mode opt-in always on
Top-level scope the global object module-local
Top-level this window undefined
import / export not allowed the whole point
Loading blocking, unless defer deferred by default
Evaluated once per tag once per URL, then cached and shared
Script vs Module A plain <script> is loose, blocks the page, and dumps its variables on window.
<script type="module"> is strict, waits for the page, keeps its variables to itself, and can import. Use modules.

One more thing worth knowing: an imported name is a live view into the module that exported it, not a copy taken once. If that module later changes the value, every importer sees the new one. That, plus imports being static and resolved before any code runs, is exactly what lets a bundler tree-shake unused exports away.

A harder ordering puzzle

Harder than the demo above: an async function, an await, and a promise chain all competing. Watch where the code after await actually goes.

async / await — where the continuation really goes
Call stack
Microtask queue
Macrotask queue
Console

Practice this layer

Opens in the editor — write it, run it, and check it against real tests.

Fix the temporal dead zone bug3 tests · beginner
↑back toThe cover↑ CovernextTypes & values→