Skip to the chapter
←JavaScriptGGroundworkJSJavaScript
JavaScript/Beginner/B1
1 / 41 written chapters
/

The cover
  1. B1Setup & mental model
  2. B2Execution context
  3. B3var, let & const
  4. B4Scope
  5. B5Single thread
  6. B6In the browser
  7. B7Types & values
  8. B8Strings & Math
  9. B9Operators & flow
  10. B10Functions
  11. B11Objects & arrays
  12. B12this
  13. B13Closures
  14. B14DOM & events
  15. B15Callbacks & promises
  16. B16Errors & tools
  17. B17Guided project
  18. ★Cheat page
  1. I1Scope & functions, properly
  2. I2Objects deeply
  3. I3Prototypes & OOP
  4. I4Async, properly
  5. I5Modules & tooling
  6. I6Regex & dates
  7. I7Error handling
  8. I8Real-time connections
  9. I9Offline & storage
  10. I10Browser APIs
  11. I11Modern JS
  12. ★Cheat page
  1. A1Engine & memory
  2. A2Advanced async
  3. A3Metaprogramming
  4. A4Types & data
  5. A5Patterns & architecture
  6. A6Performance
  7. A7Security
  8. A8Ecosystem
  9. A9Testing
  10. A10Observability
  11. A11Interview bank
  12. A12Build it yourself
  13. ★Cheat page

JSB1 · Part 1 · Beginner

Setup & mental model

The layer everybody skips — and it shows in interviews.

8 min read1 diagram9 sections1 exercise
1 of 41

In this chapter

  • Two boxes, not one
  • The 5 words that describe JS
  • Watch the single thread work
  • Where your code actually runs
  • var / let / const

and 4 more

about 8 min
09On this page
  1. Two boxes, not one
  2. The 5 words that describe JS
  3. Watch the single thread work
  4. Where your code actually runs
  5. var / let / const
  6. Naming & comments
  7. 'use strict'
  8. Script vs module
  9. A harder ordering puzzle

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

Those two rows are both consequences of one thing: before running a line, 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, and touching one early throws. The scan is called hoisting, and 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.

Dry run: the creation phase, then execution, for that snippet

StepLineabWhat happens
1creation phaseundefineduninitialised — TDZthe whole scope is scanned before line 1 runs
2console.log(a)undefinedTDZprints undefined — a was pre-filled
3console.log(b)undefinedTDZthrows ReferenceError: Cannot access 'b' before initialization — script stops here
—var a = 1;——never runs
—let b = 2;——never runs

Both lines fail for the same underlying reason — the names already existed before line 1 — but the creation phase left them in different states, so one fails silently and the other stops the whole script.

That is the whole of it you need today. The next two chapters take it apart. Execution context is the two phases behind the scan and what the TDZ actually spans; var, let and const is block scope, why a var loop and a let loop print different numbers, and why const does not freeze an object.

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

Interview answer, one sentence

"JavaScript is single-threaded and runs inside a host, the browser or Node, that supplies the event loop and APIs such as timers and fetch, so the language does one thing at a time while the host handles the waiting."

Before you move on
  • Explain the difference between the engine and the runtime, and name two APIs that exist only because the runtime provides them.
  • State the five words that describe JS, and explain why "dynamic" and "weak" are answering two different questions, not the same one.
  • Walk through why C prints before B in the event loop demo, even though the timer's delay was 0ms.
  • Predict, and explain, why console.log(a) prints undefined while console.log(b) throws, for the same var/let snippet.
  • List the concrete differences between a classic script and an ES module — strict mode, top-level scope, this, and loading order.

01Practice this chapter

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

  • 9 testsFix the temporal dead zone bugbeginner
All 35 for this level →
B1

Finished reading?

0 of 41 chapters read in this series

→Next stop

  1. Back toThe cover
  2. B1you are here
  3. Next · B2Execution contextWhat JavaScript does with your code before it runs a single line of it.11 min read

01On this page

9 sections · 0%

  1. 01Two boxes, not one
  2. 02The 5 words that describe JS
  3. 03Watch the single thread work
  4. 04Where your code actually runs
  5. 05var / let / const
  6. 06Naming & comments
  7. 07'use strict'
  8. 08Script vs module
  9. 09A harder ordering puzzle

[] chapters · t top