BSCS students taking a Web Development or JavaScript elective course — or any working developer who wants to genuinely understand JavaScript instead of just using it by pattern-matching — can read the two completed books of Kyle Simpson’s “You Don’t Know JS Yet” series, free from their official source. This page covers Get Started (a fast, whole-language survey) and Scope & Closures (a deep, mechanism-first dive into how JS’s scope system and closures actually work), 12 chapters in total.
This edition is published directly by the author on GitHub, the project’s official home, and this page is upfront about the series’ real status: You Don’t Know JS Yet was originally planned as six books, but as of today only these two — Get Started and Scope & Closures — are finished. The other four (Objects & Classes, Types & Grammar, Sync & Async, and ES.Next & Beyond) are either incomplete drafts or were never written for this edition, so this page presents exactly and only the two finished books, in full, rather than padding out unfinished material.
Book Overview
| Course | Web Development / Programming Fundamentals (JavaScript) |
| Degree Programs | BSCS and any program’s Web Development, Web Programming, or JavaScript-focused elective course, and a general reference for any course that touches client-side or Node.js JavaScript |
| Level | University — beginner-friendly start, building into intermediate language-internals material |
| Edition | 2nd Edition (ongoing series; this page covers the two completed books — Get Started and Scope & Closures) |
| Author | Kyle Simpson |
| Structure | 12 chapters across the series’ two completed books — Get Started (4 chapters) and Scope & Closures (8 chapters). The series’ other planned books (Objects & Classes, Types & Grammar, Sync & Async, ES.Next & Beyond) are not yet finished; see the note below. |
| Exercises | No formal end-of-chapter exercise sets; each book is written as continuous explanatory prose with extensive inline code examples readers are encouraged to run themselves |
| Language | English |
| License | CC BY-NC-ND 4.0 (Creative Commons Attribution-NonCommercial-NoDerivatives) — Model: Link-only |
| Format | Free, complete official Markdown/GitHub edition; a paid print/ebook edition is also sold separately |
Chapter List
Book: Get Started
Chapter 1: What Is JavaScript?
Difficulty: Beginner · Early Semester · JS Foundations · Key topics: ECMAScript & TC39, the web vs other JS environments, backwards compatibility, strict mode
This chapter frames JavaScript as far more than a browser scripting toy: it is a full, standardized language (ECMAScript, governed by the TC39 committee) that runs in browsers, on servers via Node.js, and in many other embedded environments. It stresses JS’s famous commitment to backwards compatibility — “don’t break the web” — which means old code almost never stops working, even as new syntax is added every year. It also introduces strict mode as the safer default the rest of the book assumes.
Key Points:
- JS is standardized as ECMAScript; TC39 is the committee that evolves the language through a formal proposal process.
- JS runs in many environments beyond the browser — Node.js being the most common non-browser host.
- The language has an extremely strong backwards-compatibility guarantee: old code essentially never breaks with new JS versions.
- “JS” as commonly used is technically a specific implementation of the ECMAScript specification.
- Strict mode (“use strict”) is treated as the sensible default for all modern JS code in this book series.
Memory Tip: Remember JS’s compatibility promise with the phrase “don’t break the web” — it explains why JS still supports decades-old patterns even while gaining new features every year.
Common Mistake: Assuming JavaScript and Java are related languages, or that “JavaScript” and “the browser” are the same thing — JS is a standardized language that runs in many independent environments.
Important Questions:
- Q1: What organization governs the evolution of JavaScript, and what is the language officially called? TC39 is the technical committee that manages JavaScript’s evolution through a formal proposal process; the language itself is officially standardized as ECMAScript.
- Q2: Why does JavaScript rarely break old code, even as new features are added? Because of JS’s strong backwards-compatibility commitment — often summarized as “don’t break the web” — new features are added without removing or changing the behavior of existing code, so pages written years ago generally keep working.
Chapter 2: Surveying JS
Difficulty: Beginner · Early Semester · JS Foundations · Key topics: values & types, coercion, equality, var/let/const, closures, this, prototypes, modules
A whirlwind, high-level tour of nearly every major topic the rest of the book series will cover in depth: primitive values and object types, type coercion and the difference between == and ===, variable declarations with var/let/const, functions as first-class values, closures, the this keyword, prototypes and classes, and the two module patterns (classic and ES modules). The goal is not mastery here but familiarity, so later chapters and later books have a map to refer back to.
Key Points:
- JS values are either primitives (string, number, boolean, null, undefined, symbol, bigint) or objects.
- == allows type coercion before comparing, while === compares without coercion — both are useful when understood correctly.
- let and const are block-scoped, unlike var which is function-scoped.
- Functions are first-class values in JS — they can be assigned, passed, and returned like any other value.
- Closures and the this keyword are previewed here as two of the language’s most misunderstood but important behaviors.
- JS supports two competing module patterns: the classic closure-based module pattern, and native ES modules.
Memory Tip: Treat this chapter as a checklist, not a lesson — if a term here is unfamiliar, that’s expected; Book 2 onward (Scope & Closures, and the rest of the series) is where each topic gets its full treatment.
Common Mistake: Trying to fully understand every topic (closures, this, prototypes) on first read of this chapter — it is intentionally a survey, and depth comes in later chapters and later books in the series.
Important Questions:
- Q1: What is the practical difference between == and === in JavaScript? == (loose equality) allows the operands to be coerced to a common type before comparison, while === (strict equality) compares both value and type without any coercion; == is not inherently “wrong”, but it must be used with an understanding of JS’s coercion rules.
- Q2: How do let and const differ from var in terms of scope? var declarations are function-scoped (or globally scoped if outside any function), meaning they are visible throughout the enclosing function regardless of block boundaries, while let and const are block-scoped, meaning they are only visible inside the nearest enclosing { } block.
Chapter 3: Digging to the Roots of JS
Difficulty: Beginner · Early Semester · JS Foundations · Key topics: iteration protocol, closures (precise definition), the this keyword, prototypes & delegation
This chapter goes one level deeper than the survey in Chapter 2 on four topics the author considers JS’s real roots: the iteration protocol that powers for-of loops and the spread operator, a first precise definition of closure (a function remembering and accessing variables from outside its own scope, even after that outer scope has finished executing), how this is determined dynamically by a function’s call-site rather than where the function is defined, and how prototypes enable behavior delegation between objects rather than acting like classical class inheritance.
Key Points:
- The iteration protocol defines a standard way for a value to be consumed one chunk at a time, used by for-of, spread, and destructuring.
- A closure is formally: a function remembering and continuing to access variables from its outer scope even after that outer scope has finished executing.
- this is not determined by where a function is written, but by how the function is called (its call-site).
- JS’s prototype mechanism connects objects through a chain, enabling behavior delegation rather than true classical inheritance.
- Understanding prototypes correctly avoids misapplying class-based mental models borrowed from other languages.
Memory Tip: Fix this rule in mind: this is about the call-site, not the write-site — the same function can have a completely different this value each time it’s called, depending only on how it was invoked.
Common Mistake: Assuming this refers to the function’s own lexical location (like a variable would) — this is dynamically bound per call, determined by how the function is invoked, not where it is defined (arrow functions are the one notable exception, which lexically inherit this).
Important Questions:
- Q1: What is a closure, as precisely defined in this chapter? A closure is when a function remembers and continues to access variables from its outer (enclosing) scope, even after that outer scope has already finished executing — the function “closes over” those variables rather than losing access to them.
- Q2: How does this get determined for a given function call? this is determined by the call-site — how a function is actually called at runtime — rather than by where the function is defined in the code; the same function invoked in different ways (as a plain call, as a method, with call/apply/bind, or as a constructor) can receive a different this value each time.
Chapter 4: The Bigger Picture
Difficulty: Beginner · Early Semester · JS Foundations · Key topics: scope & closures, prototypes, types & coercion, the rest of the YDKJS series
The closing chapter of Get Started zooms out and organizes everything previewed so far into three main pillars the rest of the You Don’t Know JS Yet series is built around: Scope and Closures, Objects and Classes (built on prototypes), and Types and Grammar (coercion). It also maps out the additional planned books — Sync & Async, and ES.Next & Beyond — and explains how the series as a whole is meant to be read, giving readers a clear sense of what comes next after finishing Get Started.
Key Points:
- The series is organized around three core pillars: Scope & Closures, Objects & Classes (prototypes), and Types & Grammar (coercion).
- Get Started is meant as the on-ramp; each pillar then gets its own dedicated, much deeper book.
- The planned series also includes Sync & Async (covering callbacks, promises, and async/await) and ES.Next & Beyond (covering newer language features).
- Readers are encouraged to treat the series as reference material to revisit, not a one-time linear read.
- The chapter closes the loop on the whirlwind survey from Chapter 2 by showing where each topic will be covered in full depth.
Memory Tip: Keep the three-pillar structure — Scope & Closures, Objects & Classes (prototypes), Types & Grammar (coercion) — in mind as a map for the rest of the You Don’t Know JS Yet series, even beyond what has actually been finished so far.
Common Mistake: Expecting every book in the planned series to already exist and be finished — as of this page, only Get Started and Scope & Closures are complete; see the note on the series’ current status further down this page.
Important Questions:
- Q1: What are the three main pillars the You Don’t Know JS Yet series is organized around? Scope and Closures, Objects and Classes (built on JS’s prototype mechanism), and Types and Grammar (covering type coercion) — these three pillars form the conceptual backbone the rest of the series’ planned books are each dedicated to exploring in depth.
- Q2: What is Get Started’s role relative to the rest of the series? Get Started functions as the on-ramp — it surveys the whole language at a high level (Chapter 2) and then digs slightly deeper into a few foundational roots (Chapter 3), so that a reader has enough shared vocabulary and context to go on to the deeper, pillar-specific books that follow it.
Book: Scope & Closures
Chapter 1: What is Scope?
Difficulty: Intermediate · Mid Semester · Language Internals · Key topics: compiler theory (tokenizing, parsing, code generation), engine/compiler/scope manager, LHS vs RHS lookups
This chapter builds the mental model the rest of the book depends on: JS is a compiled language (parsed and processed before execution, even though it feels interpreted), and three collaborating actors — the Engine, the Compiler, and the Scope Manager — work together to run any piece of code. It introduces the crucial distinction between an LHS (left-hand-side, target) reference, where a variable is being assigned to, and an RHS (right-hand-side, source) reference, where a variable’s current value is being retrieved, and explains how a failed RHS lookup throws a ReferenceError while a failed LHS lookup in non-strict mode can accidentally create a global variable.
Key Points:
- JS is processed through compiler-like stages (tokenizing/lexing, parsing, code generation) before it actually runs, even though it is not compiled ahead-of-time the way C or Java are.
- Three conceptual actors run every piece of JS code: the Engine (orchestrates), the Compiler (parses and generates), and the Scope Manager (tracks declarations and lookups).
- An LHS reference is a target reference — a variable being assigned a value.
- An RHS reference is a source reference — a variable whose current value is being retrieved (not literally “right-hand side” in a literal code sense, despite the name).
- A failed RHS lookup throws a ReferenceError; in non-strict mode, a failed LHS lookup can silently auto-create a global variable, which is one reason strict mode is safer.
Memory Tip: Think “target vs. source”: LHS = a variable is the target being assigned to; RHS = a variable is the source whose value is being read/retrieved.
Common Mistake: Assuming JavaScript is a purely line-by-line interpreted language with no compilation step at all — in reality, JS engines parse and process code (including hoisting declarations) before any of it actually executes.
Important Questions:
- Q1: What is the difference between an LHS and an RHS reference to a variable? An LHS (target) reference is when a variable is being assigned a value (it is the target of an assignment), while an RHS (source) reference is when a variable’s current value is being looked up and retrieved (it is the source being read from).
- Q2: What happens when an RHS lookup fails to find a declared variable, versus when an LHS lookup fails in non-strict mode? A failed RHS lookup (trying to read a variable that was never declared anywhere in the accessible scope chain) throws a ReferenceError; a failed LHS lookup (assigning to an undeclared variable) in non-strict mode does not error — it silently creates a new global variable instead, which is a common source of bugs and one reason strict mode is recommended.
Chapter 2: Illustrating Lexical Scope
Difficulty: Intermediate · Mid Semester · Language Internals · Key topics: lexical scope, author-time scope determination, nested scope bubbles
Using a series of visual metaphors (buildings with nested rooms, bubbles inside bubbles), this chapter illustrates how lexical scope works: scope is determined entirely by where functions and blocks are physically written in the source code at author-time, not by how or where a function is later called. Each nested function or block creates a new, nested scope “bubble” that can see variables in any of its enclosing scopes, but outer scopes can never see into inner ones.
Key Points:
- Lexical scope is fixed at author-time, based purely on where code is physically written — not on runtime behavior or call patterns.
- Each function or block creates a nested scope, visually representable as a bubble nested inside its enclosing scope’s bubble.
- Inner scopes can see (look up) variables from any enclosing outer scope, but outer scopes can never see into inner ones.
- This lookup only travels outward/upward through the nesting, never sideways or downward into sibling scopes.
- Because scope is author-time and static, tools (and readers) can determine a variable’s scope just by reading the source code, without running it.
Memory Tip: Picture nested bubbles or nested rooms in a building: you (an inner scope) can always see out through the walls of every room you’re inside of, but nobody outside your room can see in.
Common Mistake: Confusing lexical (author-time) scope with dynamic scope, where a function’s available variables would depend on which function called it — JS does not use dynamic scope; scope is fixed by where code is written, not by the call stack.
Important Questions:
- Q1: What does it mean that JavaScript uses lexical scope? It means scope is determined entirely by where variables and blocks/functions are physically written in the source code at author-time (i.e., when the code is typed), not by how, when, or from where a function is later called at runtime.
- Q2: Can code in an outer/enclosing scope see variables declared in an inner nested scope? No — scope lookups only travel outward: an inner scope can see variables in any of its enclosing outer scopes, but an outer scope can never look into and see variables declared inside an inner, more deeply nested scope.
Chapter 3: The Scope Chain
Difficulty: Intermediate · Mid Semester · Language Internals · Key topics: scope chain traversal, undeclared vs undefined, global scope fallback, lookup failure
This chapter explains precisely how the scope chain is traversed during a variable lookup: starting at the innermost scope and walking outward through each enclosing scope until either the variable is found or the chain is exhausted at the global scope. It draws a sharp, frequently confused distinction between “undeclared” (a variable that was never formally declared anywhere in the accessible scope chain) and “undefined” (a variable that was declared but has not yet been assigned a value), and covers what happens when a lookup exhausts the entire chain without success.
Key Points:
- A variable lookup starts at the innermost currently-executing scope and walks outward, one scope at a time, toward the global scope.
- The walk stops as soon as a matching identifier is found in any scope along the chain.
- “Undeclared” means a variable was never declared in any accessible scope; “undefined” means it was declared but has no assigned value yet — these are not the same thing, despite often being confused.
- typeof on a genuinely undeclared variable returns “undefined” without throwing, which is a historical safety quirk, not proof the variable was declared.
- If a lookup reaches the global scope and still fails, an RHS lookup throws a ReferenceError.
Common Mistake vs Memory Tip: Keep “undeclared” and “undefined” mentally separate: undeclared = never declared anywhere; undefined = declared, but no value assigned yet. They sound similar but describe very different situations.
Common Mistake: Treating a typeof check that returns “undefined” as proof a variable is declared — typeof on a completely undeclared variable also safely returns “undefined” instead of throwing, so this check cannot distinguish undeclared from declared-but-unassigned.
Important Questions:
- Q1: In what order does a JS engine search the scope chain when looking up a variable? It starts at the innermost, currently-executing scope and walks outward through each successively enclosing scope, one level at a time, continuing until either a matching identifier is found or the outermost (global) scope has been checked without success.
- Q2: What is the difference between “undeclared” and “undefined” in JavaScript? Undeclared means a variable identifier was never declared in any scope accessible from the current lookup; undefined means the variable was declared somewhere accessible, but has not yet been assigned any value — a declared-but-unassigned variable automatically holds the value undefined.
Chapter 4: Around the Global Scope
Difficulty: Intermediate · Mid Semester · Language Internals · Key topics: global scope across environments, browser vs Node.js vs web workers, ES modules and global scope, DOM globals
Global scope is not a single universal concept — this chapter surveys how it behaves differently across JS’s many hosting environments: in a browser, the global object is window (with many DOM-related globals automatically present); in Node.js, each file is effectively wrapped in its own module scope rather than sharing one true global scope directly; in web workers, there is no DOM access at all; and ES modules get their own module-level scope, separate from the traditional global scope entirely. It also flags the risk of accidentally colliding with built-in DOM globals (for example, a variable named history or name) when writing browser code.
Key Points:
- The global scope’s exact meaning and implementation differs meaningfully by JS host environment (browser, Node.js, web workers, ES modules).
- In browsers, window is the global object, and it is pre-populated with many DOM and browser-API globals a script can accidentally collide with.
- Node.js wraps each file’s top-level code in its own function/module scope by default, rather than exposing a single shared global scope the way a browser script does.
- Web workers run without any DOM access, changing which globals are actually available.
- ES modules get their own dedicated module scope, which is intentionally not the same as the traditional “global” scope.
Common Mistake: In browser code, avoid naming top-level variables after common built-in globals like name, event, status, or history — these already exist as real global/DOM properties and silently colliding with them is a classic source of confusing bugs.
Common Mistake: Assuming “global scope” means exactly the same thing in every JS environment — a browser’s window-based global scope, Node.js’s per-file module wrapping, and an ES module’s dedicated module scope are all meaningfully different, and code relying on one environment’s global behavior can break silently in another.
Important Questions:
- Q1: Why can accidentally naming a browser-side variable “name” or “history” cause bugs? Because in a browser, the global object (window) already has built-in properties with those exact names (window.name, window.history); a script-level variable using the same name can silently collide with or shadow the existing global, causing behavior that is very confusing to debug.
- Q2: How does Node.js’s handling of global scope differ from a plain browser script? Node.js effectively wraps each file’s top-level code in its own function/module scope before running it, rather than executing top-level declarations directly into one single shared global scope the way a classic browser