BSCS and BSIT students can download the complete Web Development textbook “Eloquent JavaScript” by Marijn Haverbeke. The 4th edition, published in 2024, is free to read online or download as a PDF, and it covers the JavaScript language, browser programming and Node.js in one book.
Two things set it apart from other free JavaScript books. Every code example in the online edition actually runs and can be edited in the page, so you can change a line and see what happens without leaving the chapter. And five of the twenty-one chapters are full projects rather than explanations — a delivery robot, a working programming language, a platform game, a pixel art editor and a skill-sharing website — each one building something complete from the ideas in the chapters before it.
Book Overview
| Course | Web Development / JavaScript Programming |
| Degree Programs | BSCS, BSIT |
| Level | University — no prior programming experience assumed |
| Edition | 4th edition (2024) |
| Author | Marijn Haverbeke |
| Structure | Introduction plus 21 chapters in three parts: Language, Browser, Node |
| Project Chapters | 5 — a robot, a programming language, a platform game, a pixel art editor, a skill-sharing website |
| Language | English |
| License | CC BY-NC 3.0 for the text; the code is separately MIT licensed — Model: Link-only |
| Format | Free online HTML with runnable code, plus PDF, EPUB and MOBI |
Chapter List
Introduction
Difficulty: Easy · Key topics: what programming is, why language matters, what JavaScript is, how to read the book
Haverbeke opens by admitting the thing most books avoid: programming is hard. The rules are simple and clear, but programs built on them become complex enough to grow their own rules, so you end up building your own maze. He then shows why languages matter by displaying a program as raw machine instructions and rewriting it in JavaScript. The history section explains that JavaScript was introduced in 1995 for Netscape Navigator, was adopted by every other browser, and made modern web applications possible. He is honest about the language’s flaws while arguing that its flexibility, once understood, is a strength. The chapter closes with the book’s own map: twelve chapters on the language, seven on the browser, two on Node, and five project chapters spread among them.
Key Points:
- JavaScript appeared in 1995 in Netscape Navigator and was later adopted by every major browser.
- The book has three parts: the language (Chapters 1–12), the browser (13–19), and Node.js (20–21).
- Five chapters are projects rather than explanations, each building a complete working program.
- The author’s advice is to read code attentively rather than glance over it — reading and writing code are both essential.
- Every example in the online edition runs and can be edited in place.
Practice Tip: Open the online edition rather than the PDF while you are learning. Being able to change a value in an example and immediately see the new output is the single biggest advantage this book has over any other free JavaScript text.
Common Mistake: Skimming the code blocks. The author says directly that this will feel slow and confusing at first — that is expected, and pushing through it is the point.
Important Questions:
- When and why was JavaScript created? In 1995, to add programs to web pages in the Netscape Navigator browser; it was then adopted by all other major browsers and made interactive web applications possible.
- How is this book organised? Into three parts — the JavaScript language, programming the browser, and Node.js — with five project chapters distributed among them.
Chapter 1: Values, Types, and Operators
Part 1: Language · Difficulty: Easy · Key topics: numbers, strings, template literals, unary operators, Booleans, null and undefined, automatic type conversion
The chapter starts from bits — an ocean of them, more than a hundred billion in a typical computer’s working memory — and explains that we cope by grouping them into values. Numbers come first: JavaScript uses a fixed 64 bits per number, which is why very large integers and fractional arithmetic have limits. Strings follow, written with single quotes, double quotes or backticks, with backticks allowing embedded expressions. The unary operator typeof produces a string naming a value’s type. Booleans have exactly two values and come from comparisons. Then the two empty values, null and undefined, which both mean “no meaningful value”. The chapter ends on automatic type conversion, where JavaScript quietly converts a value rather than complaining — the source of its most famous oddities.
Key Points:
- JavaScript stores every number in 64 bits, so there is a limit to precision and to integer size.
- Strings can be written with single quotes, double quotes or backticks; backticks allow embedded expressions.
typeofis a unary operator that returns a string naming the type of its operand.nullandundefinedboth denote the absence of a meaningful value, and the difference between them is mostly historical.- When an operator gets the wrong type, JavaScript converts the value instead of raising an error — this is type coercion.
- Use
===and!==, which do not convert types, rather than==and!=, which do.
Practice Tip: Type the odd comparisons from the automatic-conversion section into the console yourself — 8 * null, "5" - 1, "5" + 1, null == undefined. Predicting each answer before you press enter teaches coercion faster than reading the rules.
Common Mistake: Using == out of habit. It converts types before comparing, which is where surprises like "" == 0 being true come from; === compares without converting.
Important Questions:
- What is the difference between
==and===?==converts the operands to a common type before comparing, while===compares both value and type without converting. - What is the difference between
nullandundefined? Both represent the absence of a meaningful value;undefinedis what the language produces when nothing meaningful exists, andnullis usually assigned deliberately by the programmer.
Chapter 2: Program Structure
Part 1: Language · Difficulty: Easy · Key topics: expressions and statements, bindings, the environment, console.log, control flow, if, while, for, break, switch, comments
Chapter 1 made values; this chapter frames them into programs. The key distinction is stated early: a fragment of code that produces a value is an expression, while a statement is a complete instruction. To hold values, JavaScript provides bindings — the book’s preferred word for variables — created with let, const or the older var. The collection of bindings that exists at a given moment is called the environment, and it is never empty, because it always contains the standard bindings the language and the surrounding system provide. Control flow follows: conditional execution with if, loops with while, do and for, escaping a loop with break, and dispatching on a value with switch — which the author admits has awkward syntax inherited from C and Java. Indentation, capitalisation conventions and comments close the chapter.
Key Points:
- An expression produces a value; a statement is a complete instruction. A program is a list of statements.
- A binding (variable) is created with
let,constorvar;constcannot be reassigned. - The environment is the collection of bindings that exists at a given time, and it always contains the standard ones.
- A binding name cannot start with a digit and cannot be a keyword such as
let. forgathers the counter setup, test and update into one line — the same pattern awhileloop would spread out.breakjumps straight out of the enclosing loop, whatever the loop condition says.- Indentation is optional for the computer and essential for the reader.
Practice Tip: Do the FizzBuzz and chessboard exercises at the end. They look trivial, but they are the first time you must combine loops, conditionals and string building, and getting the chessboard to print correctly catches almost every off-by-one habit early.
Common Mistake: Thinking const makes a value unchangeable. It only stops the binding from being reassigned — the contents of a const object or array can still be modified, as Chapter 4 shows.
Important Questions:
- What is the difference between an expression and a statement? An expression is a fragment of code that produces a value; a statement is a complete instruction, and a program is a list of statements.
- What is the environment in JavaScript? The collection of bindings and their values that exists at a given time, which always includes the bindings provided by the language standard and the surrounding system.
Chapter 3: Functions
Part 1: Language · Difficulty: Medium · Key topics: defining functions, scope, nested scope, functions as values, declarations, arrow functions, the call stack, optional arguments, closure, recursion
A function definition is just a binding whose value happens to be a function, and the chapter takes that idea seriously. It shows the three ways to write one — a function expression, a function declaration, and an arrow function — and explains that a declaration is hoisted, so it can be used before the line that defines it. Scope is covered carefully: bindings outside any function or block are global, those inside are local, and blocks and functions can nest to any depth, with inner code able to see outer bindings but not the reverse. The call stack is explained as the place the computer remembers where to return, which makes stack overflow understandable rather than mysterious. Optional arguments come next, since JavaScript silently ignores extra arguments and passes undefined for missing ones. Then closure — a function remembering the local bindings that existed when it was created — and recursion, with an honest note that it is usually slower than a loop but often clearer.
Key Points:
- A function is a value, so it can be stored in a binding, passed as an argument and returned from another function.
- Function declarations are hoisted to the top of their scope; function expressions and arrow functions are not.
- Bindings declared inside a function or block are local; those declared outside any of them are global.
- Inner scopes can see the bindings of outer scopes, but not the other way round.
- JavaScript ignores extra arguments and gives missing parameters the value
undefined. - A closure is a function that keeps access to the local bindings that existed when it was created.
- Recursion is usually slower than looping, but for some problems it is far easier to read.
Practice Tip: Write the wrapValue closure example yourself and then call the returned function after the outer one has finished. Seeing a local binding survive the call that created it is what makes closure click; reading the definition rarely does.
Common Mistake: Assuming an arrow function is only a shorter function. It also does not have its own this, which matters as soon as you write methods in Chapter 6.
Important Questions:
- What is a closure? A function that continues to have access to the local bindings that were in scope when it was created, even after the call that created them has finished.
- What is the call stack? The place the computer stores the context it must return to after each function call; running out of space in it causes a stack overflow.
Chapter 4: Data Structures — Objects and Arrays
Part 1: Language · Difficulty: Medium · Key topics: arrays, properties, methods, objects, mutability, rest parameters, the Math object, destructuring, optional chaining, JSON
The chapter is built around a running story — Jacques, who turns into a squirrel — and his journal, which needs a real data structure to analyse. Arrays come first, then the idea that most values have properties, accessed with a dot or with square brackets when the name is computed. Methods are simply properties that hold functions. Objects group related values under names. Then the central idea of the chapter: mutability. Numbers, strings and Booleans are immutable, but objects and arrays are not, and two objects with identical contents are still different objects, because comparison checks identity rather than contents. The correlation analysis of the journal follows, then a run of modern conveniences: rest parameters with three dots, the Math object as a namespace, destructuring to pull array elements or object properties straight into bindings, optional property access with ?., and finally JSON for turning data into text and back.
Key Points:
- Properties are accessed with
value.propor, when the name is computed, withvalue["prop"]. - A method is simply a property whose value is a function.
- Numbers, strings and Booleans are immutable; objects and arrays can be changed in place.
- Comparing objects with
==compares identity, not contents — two separate objects with the same properties are not equal. - A
constbinding to an object still allows the object’s contents to change. - Rest parameters (
...args) collect any number of remaining arguments into an array. - JSON has no functions, no dates and no bindings — only the plain data types, with double quotes required around property names.
Practice Tip: Do the weresquirrel correlation exercise all the way through rather than reading it. It is the first time in the book that you hold real data in a structure and compute something meaningful from it, and it is what makes objects feel useful instead of theoretical.
Common Mistake: Expecting {a: 1} == {a: 1} to be true. Object comparison tests whether the two references point at the same object, not whether their contents match.
Important Questions:
- What does mutability mean in JavaScript? Objects and arrays can have their contents changed after creation, while numbers, strings and Booleans cannot — they can only be replaced.
- What is JSON? A text format for serializing data, using only the plain data types with double-quoted property names; functions, dates and bindings cannot be represented in it.
Chapter 5: Higher-Order Functions
Part 1: Language · Difficulty: Medium · Key topics: abstraction, higher-order functions, filter, map, reduce, composability, the SCRIPTS dataset
The chapter opens with an analogy that earns its place: two recipes for pea soup, one listing every physical action and one written in ordinary cooking vocabulary. The second is shorter not because it does less but because it uses abstractions. In programming, functions that operate on other functions — taking them as arguments or returning them — are higher-order functions, and because functions are already values there is nothing exotic about them. The chapter then works through a real dataset of writing systems (Latin, Cyrillic, Arabic and so on) using the three that matter most: filter keeps the elements that pass a test, map transforms every element into a new one, and reduce collapses a whole array into a single value. Composability is the payoff — chaining these reads far better than the equivalent nested loops — with an honest note that the composed version does more work and is not always the right choice.
Key Points:
- A higher-order function takes a function as an argument or returns one.
filterreturns a new array of the elements that pass a test; it does not modify the original.mapreturns a new array of the same length with every element transformed.reducecombines all elements into a single value, taking a combining function and a start value.someandeveryreturn a Boolean for whether any or all elements pass a test.- Chained higher-order calls are more readable than nested loops but do more work — a real trade-off, not a free win.
Practice Tip: Take a loop you have already written and rewrite it three times, once each with filter, map and reduce. These three methods appear in almost every real JavaScript codebase, and converting your own code is the fastest way to stop reaching for a for loop by reflex.
Common Mistake: Forgetting that map and filter return new arrays rather than changing the original. Calling them without using the result does nothing at all.
Important Questions:
- What is a higher-order function? A function that operates on other functions, either by taking them as arguments or by returning them.
- What is the difference between
mapandfilter?maptransforms every element and returns an array of the same length;filterkeeps only the elements that pass a test, so the result may be shorter.
Chapter 6: The Secret Life of Objects
Part 1: Language · Difficulty: Hard · Key topics: abstract data types, methods, this, prototypes, classes, private properties, Maps, polymorphism, getters and setters, symbols, iterators, inheritance
The longest conceptual chapter in Part 1, and the one students most often need twice. Object-oriented programming is presented as using types of objects as the unit of program organisation. Methods are properties holding functions, and when one is called as object.method() the binding this points at the object it was called on. Prototypes come next — the mechanism underneath JavaScript’s objects, where an object can fall back to another object for properties it does not have itself. Classes are then shown as a cleaner notation over that same prototype system, not a different mechanism. Private properties are declared with a # prefix. Maps are introduced as the right structure for arbitrary keys, as opposed to plain objects. Polymorphism, getters, setters, statics and symbols follow, then the iterator interface that makes for/of work on your own classes, and finally inheritance and instanceof.
Key Points:
- A method is a property holding a function; called as
object.method(), itsthisrefers to that object. - A prototype is another object that an object falls back to for properties it does not have itself.
- Class notation is a cleaner way to write the prototype system — it is not a separate mechanism.
- A property name beginning with
#is private and accessible only inside the class. - Use a
Maprather than a plain object when keys are arbitrary values. - Polymorphism means different types can be used through the same interface.
- Defining a method with
Symbol.iteratormakes your own class work withfor/of.
Memory Tip: Remember that this is decided by how a function is called, not where it is written. Called as obj.method(), this is obj; pulled out and called on its own, it is not. That one rule explains most this bugs you will ever hit.
Common Mistake: Passing a method as a callback — setTimeout(obj.method, 100) — and losing this. The function is no longer being called on the object, so bind it or wrap it in an arrow function.
Important Questions:
- What is a prototype in JavaScript? Another object that an object falls back to for properties it does not have itself, which is the mechanism underlying JavaScript’s classes and inheritance.
- What does
thisrefer to inside a method? The object the method was called on — determined by how the function is called, not by where it is defined.
Chapter 7: Project — A Robot
Part 1: Language · Difficulty: Hard · Key topics: graphs, state as a value, persistent data structures, simulation, pathfinding
The first project chapter. A village of eleven places joined by fourteen roads forms a graph, and a delivery robot must pick up and deliver parcels by deciding where to go next at each step. The design decision the chapter is really teaching is to model the village state as a persistent, immutable value: moving does not modify the state, it returns a new state. The author is explicit that this is unusual in JavaScript, where almost everything can be changed, and explains why it makes the program easier to reason about. Three robots are then compared — one moving at random, one following a fixed route through every place twice, and one that uses breadth-first search to find a route to the nearest parcel. Watching the third beat the second, and the second beat the first, is the point of the chapter.
Key Points:
- A graph is a collection of points with connections between them — here, places and roads.
- A persistent or immutable data structure is never modified; operations return a new value instead.
- Modelling state as a value rather than something you mutate makes a program much easier to reason about.
- A robot is modelled as a function from the current state plus memory to the next move.
- The random robot works but takes many turns; a fixed route is better; pathfinding is better still.
- Breadth-first search finds the shortest route by exploring all places one step away, then two, and so on.
Practice Tip: Do the compareRobots exercise, which runs two robots over the same hundred random tasks. Measuring rather than guessing which strategy is better is the habit this chapter is really trying to build.
Common Mistake: Modifying the state object inside a move and wondering why the simulation misbehaves. The whole design depends on returning a new state instead.
Important Questions:
- What is a persistent data structure? One that is never modified after creation — operations that would change it return a new value instead, leaving the original intact.
- How does the pathfinding robot work? It uses breadth-first search over the road graph to find the shortest route to the nearest parcel or destination, then follows that route step by step.
Chapter 8: Bugs and Errors
Part 1: Language · Difficulty: Medium · Key topics: strict mode, types and TypeScript, testing, debugging, exceptions, try/catch/finally, selective catching, assertions
The chapter begins with an uncomfortable truth: JavaScript’s looseness means the language will rarely catch a typo before running the program. Strict mode, enabled with "use strict" at the top of a file or function, tightens some of this — and code inside classes and modules is automatically strict. Static type checking is discussed honestly, with TypeScript named as the widely used option. Testing comes next, with the argument that checking by hand repeatedly is both annoying and ineffective. Debugging is treated as a method rather than a reflex: form a hypothesis about what is wrong, then test it, rather than changing code at random. Exceptions follow — how throw unwinds the stack, how try/catch handles the problem, why finally matters when a function has side effects, and why you should catch specific error types rather than everything. Assertions close the chapter as checks for programmer mistakes rather than for expected conditions.
Key Points:
- Strict mode is enabled with
"use strict"; classes and modules are strict automatically. - An exception thrown by
throwunwinds the call stack until acatchblock handles it. - A
finallyblock runs whether or not an exception occurred — use it to clean up. - Catch specific error types, not everything; a blanket
catchhides bugs you needed to see. - Define your own
Errorsubclasses so callers can distinguish your errors from the language’s. - Assertions check for programmer mistakes, not for conditions expected during normal operation.
- Debug by forming and testing a hypothesis, not by changing code at random.
Practice Tip: Write the retry exercise, which wraps a function that fails eighty per cent of the time and keeps calling it until it succeeds. It forces you to catch one specific exception type and let every other error through — exactly the discipline the chapter is teaching.
Common Mistake: Writing catch (e) {} to make an error message go away. That silences every error including the ones you needed to know about, which is why the chapter insists on selective catching.
Important Questions:
- What does a
finallyblock do? It runs after thetryblock whether or not an exception was thrown, which makes it the right place to release resources or undo partial changes. - Why should you catch specific exception types? Because catching everything also swallows unrelated bugs, hiding real problems instead of handling the one case you meant to handle.
Chapter 9: Regular Expressions
Part 1: Language · Difficulty: Hard · Key topics: creating regexes, test and exec, character sets, repetition, groups, the Date class, boundaries, choice, backtracking, replace, greed, lastIndex
Regular expressions are introduced as a small language of their own embedded inside JavaScript, written either as a literal between slashes or with the RegExp constructor. The chapter is unusually thorough about the mechanics rather than just the syntax. Character sets in square brackets, repetition with +, *, ? and {n,m}, grouping with parentheses, and extracting matched pieces with exec and capture groups all come first. The Date class is covered here because date parsing is the running example, including the trap that JavaScript month numbers start at zero. Then the parts most books skip: boundaries and look-ahead to force a match to span the whole string, the pipe for choice, and a real explanation of how the engine backtracks through alternatives — which is what makes some patterns catastrophically slow. Greedy versus non-greedy repetition, dynamic pattern construction, and the lastIndex quirk of global regexes complete it.
Key Points:
- A regex is written between slashes or built with
new RegExp()when the pattern is dynamic. testreturns a Boolean;execreturns a match object with the matched text and any groups, ornull.- Parentheses create a capture group, which lets you extract the part that matched.
^and$anchor to the start and end of the string, forcing a whole-string match.- Repetition operators are greedy by default; adding
?after them makes them match as little as possible. - JavaScript’s
Datecounts months from zero, so December is 11. - A global (
g) regex keeps alastIndexbetween calls, which causes surprising results if the same object is reused.
Memory Tip: Greedy versus lazy is one character. Repetition takes as much as it can by default; adding ? after it makes it take as little as possible. Every “why did my pattern match too much” question is this one difference.
Common Mistake: Reusing one global regex object across several calls to test. Its lastIndex carries over, so the same string can return true and then false, which the chapter warns about explicitly.
Important Questions:
- What is the difference between
testandexec?testreturns only a Boolean saying whether the pattern matched;execreturns an object containing the matched string, its position and any capture groups, ornullif there was no match. - What does greedy matching mean? Repetition operators match as much text as they can while still allowing the rest of the pattern to match; adding
?makes them non-greedy so they match as little as possible.
Chapter 10: Modules
Part 1: Language · Difficulty: Medium · Key topics: modular programs, ES modules, import and export, packages, NPM, CommonJS, bundling, module design
A module is defined as a piece of program that states which other pieces it depends on and which functionality it provides — its interface. The chapter is partly history, and that history explains the mess students meet in real projects. The original language had no modules at all: every script shared one scope, which encouraged accidental entanglement. The community built CommonJS on top of functions, using require, and Node adopted it. In 2015 the language finally gained its own system, ES modules, with import and export. Both are still in use, which is why you meet both. Packages and NPM follow as the way to reuse code across projects, then building and bundling, and why many packages are not technically written in JavaScript at all — TypeScript and not-yet-standard features have to be compiled down first. The chapter closes on module design, which the author admits is subjective and full of trade-offs.
Key Points:
- A module declares its dependencies and its interface — the part other modules can use.
- ES modules use
importandexportand were added to the language in 2015. - CommonJS uses
requireandmodule.exportsand predates the language’s own system. - ES module imports are resolved before the module runs, so they cannot be conditional;
requireis an ordinary function call. - NPM is both the online repository of packages and the command line tool that installs them.
- Bundlers combine many small modules into one file; compilers turn TypeScript or new syntax into runnable JavaScript.
- Good module design is subjective — the aim is a small, clear interface with few dependencies.
Memory Tip: Tell the two systems apart by their keywords and their era. require is CommonJS, invented by the community for Node before the language had modules; import is the official ES module system added in 2015. Seeing require in a codebase tells you its age.
Common Mistake: Mixing require and import in one file. They are different systems with different loading rules, and combining them is a common source of confusing errors in Node projects.
Important Questions:
- What is a module? A piece of a program that specifies which other pieces it depends on and which functionality it provides to others through its interface.
- What is the difference between ES modules and CommonJS? ES modules are the language’s own system using
import/export, resolved before the module runs; CommonJS is the older community system usingrequire, which is an ordinary function call.
Chapter 11: Asynchronous Programming
Part 1: Language · Difficulty: Hard · Key topics: asynchronicity, callbacks, promises, error handling, async/await, generators, the event loop, asynchronous bugs
The distinction is put clearly at the start: in a synchronous model a long-running action stops the whole program until it finishes, while an asynchronous model lets other things happen while waiting. The chapter then walks the three generations of solution in order. Callbacks come first — pass a function to be called when the work is done — along with the nesting problem that follows when several async steps depend on each other. Promises replace them with an object representing a future result, which composes better and lets failures propagate like exceptions. Then async and await, which let you write code that looks linear while still being asynchronous. Generators are introduced as the same pause-and-resume ability without the promises. The event loop is explained as what actually schedules all of this: the main script runs to completion, then callbacks run one at a time, never interrupting each other. The chapter ends on asynchronous bugs, where state changes between the gaps in your program’s execution.
Key Points:
- A synchronous call blocks the whole program until it finishes; an asynchronous one lets other work continue.
- A promise is an object representing a value that is not available yet.
async/awaitis syntax over promises — anasyncfunction always returns a promise.- Use
try/catcharoundawaitto handle rejected promises the same way as ordinary exceptions. - A generator function is written
function*and can pause atyieldand resume later. - The event loop runs the main script to completion, then each callback to completion — callbacks never interrupt each other.
- Asynchronous bugs come from state changing during the gaps when your function is suspended.
Memory Tip: Remember that await does not pause the program, only the function it is in. Everything else keeps running. Half of all confusion about async code comes from imagining the whole program stops.
Common Mistake: Forgetting await and getting a Promise where you expected a value. It does not throw an error — you simply get an object with the wrong shape, which surfaces much later.
Important Questions:
- What is a promise? An object representing a value that is not available yet; it either resolves with a value or rejects with a reason, and handlers are attached with
thenor awaited. - What does the event loop do? It runs the main script to completion and then runs scheduled callbacks one at a time, each to completion, so no callback ever interrupts another.
Chapter 12: Project — A Programming Language
Part 1: Language · Difficulty: Hard · Key topics: parsing, syntax trees, evaluators, special forms, scope objects, functions, compilation
The most ambitious chapter in Part 1: you build a small working language called Egg, in JavaScript, from nothing. The parser reads text and produces a syntax tree reflecting the program’s structure, pointing at the problem if the text is not a valid program. The evaluator then takes that tree and a scope object mapping names to values, and produces a result. Special forms — if, while, define, fun — are handled separately because they cannot simply evaluate all their arguments first. The environment is an ordinary object whose properties are the bindings. Functions get their own local scope. The chapter closes with two honest sections: compilation, adding a step between parsing and running so the program becomes something faster to evaluate, and “Cheating”, where the author admits that Egg’s if and while are thin wrappers over JavaScript’s own, and that its values are just JavaScript values.
Key Points:
- A parser turns program text into a syntax tree that reflects its structure.
- An evaluator walks that tree with a scope object and produces the program’s result.
- Special forms cannot evaluate all their arguments first —
ifmust not evaluate both branches. - A scope is just an object whose properties are the bindings visible at that point.
- An interpreter acts directly on the parsed representation; a compiler adds a transformation step first.
- The author states openly that Egg’s constructs are wrappers over JavaScript’s, not a from-scratch machine.
Practice Tip: Do the “Fixing scope” exercise, which adds a set special form that assigns to an existing binding in an outer scope and raises an error otherwise. It is the exercise that forces you to actually understand how scope chains work, in Egg and in JavaScript.
Common Mistake: Treating this chapter as optional because you will never write a language. The parsing and tree-walking techniques here are exactly what you use for configuration formats, template engines and expression evaluators — and it makes Chapter 14’s DOM tree feel familiar.
Important Questions:
- What does a parser do? It reads program text and produces a data structure — a syntax tree — reflecting the structure of the program, reporting an error if the text is not valid.
- What is the difference between an interpreter and a compiler? An interpreter evaluates the parsed representation of a program directly; a compiler adds a step that transforms the program into a form that can be evaluated more efficiently.
Chapter 13: JavaScript and the Browser
Part 2: Browser · Difficulty: Easy · Key topics: networks and the internet, the Web, HTTP and URLs, HTML, the script tag, sandboxing, browser compatibility
Part 2 opens with the platform rather than the code. Networks date from the 1950s; connecting machines lets them exchange data, and the internet is what happens when that connection becomes global. The Web is then distinguished from the internet: it is a set of protocols and formats for visiting linked pages, running on top of the internet. HTML is introduced as the document format, with tags giving structure to text. The <script> tag is the important one for this book, running JavaScript as soon as the browser reaches it. Sandboxing is explained as the answer to an obvious danger — you are running programs written by strangers — so browser JavaScript is deliberately restricted from touching your files or other sites’ data. The chapter ends with the browser wars, which explains why compatibility is still a concern and why standards matter.
Key Points:
- The internet is the global network; the Web is the set of protocols and formats for linked pages running on it.
- HTML gives structure to text through tags describing links, paragraphs and headings.
- A
<script>tag runs its JavaScript as soon as the browser encounters it while reading the HTML. - Sandboxing restricts browser JavaScript so a page cannot read your files or another site’s data.
- When one browser dominated, its vendor could ignore standards — which is why compatibility problems persist.
- Browser JavaScript is deliberately limited: the restrictions are a security feature, not an oversight.
Practice Tip: Write a two-line HTML file with a <script> tag and open it in your browser with the developer console showing. Every remaining chapter of Part 2 assumes you can do this instantly, and it takes about a minute to set up.
Common Mistake: Using “the internet” and “the Web” interchangeably. The internet is the network; the Web is one thing built on top of it, alongside email and many others.
Important Questions:
- What is the difference between the internet and the Web? The internet is the global network of connected computers; the Web is a set of protocols and formats for linked documents that runs on top of it.
- What is browser sandboxing and why does it exist? The restriction of browser JavaScript so it cannot access local files or other sites’ data — necessary because you run programs written by people you have no reason to trust.
Chapter 14: The Document Object Model
Part 2: Browser · Difficulty: Medium · Key topics: document structure, trees, node types, navigating and finding elements, creating and changing nodes, attributes, layout, CSS, query selectors, animation
An HTML document is a nested set of boxes, and the browser represents it as a tree of objects — the DOM. The chapter connects this deliberately to the syntax trees from Chapter 12: the shape is the same, nodes referring to children that have children of their own. It is honest about the interface being awkward: the DOM was designed to be language-neutral rather than JavaScript-friendly, which is why node types are numeric codes and collections are array-like without being arrays. Navigating by parentNode and childNodes is shown, then rejected as fragile, in favour of getElementsByTagName and especially querySelector and querySelectorAll, which take CSS selectors. Changing the document follows — remove, appendChild, insertBefore, replaceChild, creating nodes with createElement and createTextNode. Then attributes, block versus inline layout, reading positions and sizes, styling from JavaScript, CSS and the cascade, and finally simple animation.
Key Points:
- The DOM is the browser’s tree of objects representing the document; JavaScript can read and change it.
- The interface is language-neutral rather than JavaScript-friendly, which is why it feels clumsy.
querySelectorreturns the first match for a CSS selector;querySelectorAllreturns all of them.- Navigating by fixed paths of
childNodesis fragile — whitespace between tags creates text nodes. - A node created with
createElementexists but appears nowhere until it is inserted into the tree. - Block elements take the full width and start a new line; inline elements sit within a line of text.
- Changing the DOM causes the browser to recompute layout, which is why doing it inside a loop is slow.
Practice Tip: Open any real website, press F12, and use document.querySelectorAll in the console to select and restyle its elements. Changing a live page you did not write is the fastest way to make the DOM feel concrete rather than abstract.
Common Mistake: Assuming childNodes contains only elements. Whitespace and line breaks between tags become text nodes too, which is why children or a query selector is usually what you actually want.
Important Questions:
- What is the DOM? The Document Object Model — the tree of objects the browser builds to represent an HTML document, which JavaScript can read and modify.
- What is the difference between
querySelectorandquerySelectorAll?querySelectorreturns the first element matching a CSS selector, whilequerySelectorAllreturns a collection of all matching elements.
Chapter 15: Handling Events
Part 2: Browser · Difficulty: Medium · Key topics: event handlers, event objects, propagation, default actions, key and pointer events, scroll and focus events, timers, debouncing
The chapter opens by showing why polling is the wrong model: to catch a keypress by reading key state you would have to check constantly and would still miss things. Events invert that — the browser tells you. Handlers are registered with addEventListener on window or on any DOM element, and only fire for events in that element and its children. Every handler receives an event object with details such as which mouse button was pressed. Propagation is covered properly: an event moves outward from the element where it happened through each ancestor, so a handler on a paragraph also sees clicks on a button inside it, and stopPropagation halts that. Default actions — following a link, scrolling on arrow keys — run after handlers unless preventDefault is called. Key, pointer, scroll, focus and load events each get a section. The chapter ends by connecting events to the event loop from Chapter 11, then covers timers and debouncing.
Key Points:
- Handlers are registered with
addEventListenerand removed withremoveEventListener. - Every handler receives an event object describing what happened.
- Events propagate outward from the element where they occurred through each ancestor.
stopPropagationhalts that outward travel;preventDefaultcancels the browser’s default action.- Focus and blur events do not propagate, unlike most other events.
- Handlers are scheduled on the event loop and wait for running scripts to finish before they execute.
- Debouncing groups rapid repeated events so an expensive handler runs once instead of hundreds of times.
Memory Tip: Keep the two cancel methods apart by what they stop. preventDefault stops the browser from doing its own thing, such as following a link. stopPropagation stops the event from reaching ancestor elements. They are unrelated, and mixing them up is one of the most common bugs in this chapter.
Common Mistake: Attaching a handler to every item in a long list. Because events propagate, one handler on the parent that checks event.target does the same job with far less work.
Important Questions:
- What is event propagation? After an event fires on an element it travels outward to each ancestor, so handlers on parent elements also receive it unless
stopPropagationis called. - What is the difference between
preventDefaultandstopPropagation?preventDefaultcancels the browser’s built-in response to the event;stopPropagationstops the event from being passed on to ancestor elements.
Chapter 16: Project — A Platform Game
Part 2: Browser · Difficulty: Hard · Key topics: level representation, actors, DOM-based drawing, motion and collision detection, tracking keys, animation loop
A complete side-scrolling platform game, based on Thomas Palef’s Dark Blue, built with the DOM rather than canvas. Levels are written as plain strings where each character is a tile or a starting position, which makes them human-editable. A Level class parses that string; actors — player, coins, moving lava — all share one interface with pos, size and an update method. The drawing logic is deliberately put behind an interface so the next chapter can swap in a canvas display without touching the game. Motion works by splitting time into small steps and moving each actor by speed multiplied by the step, checking for collisions before committing the move. Key tracking stores which arrow keys are currently held rather than reacting to individual presses. The animation loop uses requestAnimationFrame, wrapped to make the time step available.
Key Points:
- Levels are stored as strings, one character per tile, which keeps them readable and editable.
- All actors share one interface, so the game logic does not need to know their specific types.
- Drawing sits behind an interface, so the display can be replaced without changing the game.
- Motion is computed per time step as speed multiplied by the step, not per frame.
- A move is tested against obstacles before it is applied, and rejected if it collides.
- Key handling stores which keys are currently held down, since movement continues while a key is pressed.
requestAnimationFramedrives the loop, giving one call per frame the browser is ready to draw.
Practice Tip: Do the “Pausing the game” exercise first, before the harder ones. Adding a pause on Escape forces you to understand how the animation loop starts and stops, which is the piece everything else in the chapter depends on.
Common Mistake: Moving actors by a fixed amount per frame instead of per unit of time. The game then runs at different speeds on different machines, which is precisely why the chapter multiplies speed by the time step.
Important Questions:
- Why is motion computed from the time step rather than per frame? Because frame rates differ between machines; multiplying speed by elapsed time keeps the game running at the same real-world speed everywhere.
- Why is the drawing code separated behind an interface? So the same game logic can be displayed in different ways — the next chapter replaces the DOM display with a canvas one without changing the game itself.
Chapter 17: Drawing on Canvas
Part 2: Browser · Difficulty: Hard · Key topics: SVG versus canvas, fills and strokes, paths, curves, pie charts, text, images and sprites, transformations, save and restore
Three ways to draw in a browser are compared: HTML with styling, SVG, and canvas. SVG produces a DOM of shapes you can interact with; canvas is a single element you draw pixels into, which is faster but leaves nothing to click on. The canvas API is then covered systematically. Shapes are filled or stroked. Paths are described through side effects — a sequence of method calls rather than a value you can store and reuse, which the author calls peculiar and it is. Curves get their own section, then a worked pie chart, then text and images. Sprites are drawn by cutting rectangles out of a larger image with drawImage. Transformations — translate, scale, rotate — change everything drawn after them, so the chapter covers save and restore to keep them contained. It ends by rebuilding the previous chapter’s game display on canvas, proving that the interface separation was worth it.
Key Points:
- SVG gives you a DOM of shapes you can interact with; canvas gives you pixels and no elements.
- Canvas is faster for many moving objects; SVG is better when shapes need to be clickable.
- Paths are built through a sequence of method calls, not stored as values.
drawImagewith the extra arguments cuts one rectangle out of an image, which is how sprites work.- Transformations apply to everything drawn afterwards until they are reset.
saveandrestorepush and pop the transformation state, keeping changes contained.- The same game from Chapter 16 works unchanged with a canvas display, because drawing was behind an interface.
Memory Tip: Choose between canvas and SVG by asking whether anything needs to be clicked. Clickable shapes mean SVG, because they are real DOM elements. Thousands of moving pixels mean canvas, because there are no elements to slow it down.
Common Mistake: Forgetting to call restore after transforming. Every later drawing stays rotated or scaled, and the resulting bug looks nothing like its cause.
Important Questions:
- When should you use canvas instead of SVG? When you are drawing many moving elements and do not need them to be individually clickable; SVG is better when shapes must respond to events or be styled with CSS.
- What do
saveandrestoredo? They store and reinstate the canvas’s current transformation state, so a transformation applied for one drawing does not affect everything drawn afterwards.
Chapter 18: HTTP and Forms
Part 2: Browser · Difficulty: Hard · Key topics: the HTTP protocol, methods and status codes, fetch, sandboxing and CORS, HTTPS, form fields, focus, text fields, checkboxes and selects, client-side storage
The chapter starts by tracing what happens when you type a URL: the browser looks up the server’s address, opens a TCP connection on port 80, and exchanges a request and a response. Methods, paths, headers, bodies and status codes are all covered from real examples. fetch is the modern interface for making requests from JavaScript, returning a promise that resolves to a Response object — which connects directly back to Chapter 11. Sandboxing reappears here as CORS: a script on one site cannot freely request data from another, and the reason is spelled out plainly. HTTPS is explained as the encrypted version and why it matters on hostile networks. The second half is forms: fields are DOM elements with a value property, focus determines where keyboard input goes, and each field type — text, checkbox, radio, select, file — has its own interface. The chapter closes on localStorage and sessionStorage.
Key Points:
- An HTTP request has a method, a path, headers and optionally a body; the response has a status code, headers and a body.
- GET reads, POST sends new data, PUT replaces and DELETE removes.
- Status code families: 2xx success, 3xx redirection, 4xx client error, 5xx server error.
fetchreturns a promise resolving to aResponseobject.- CORS stops a script on one site from freely reading data from another, unless that server allows it.
- HTTPS encrypts traffic so it cannot be read or modified along the way.
localStoragepersists after the browser closes;sessionStoragelasts only for the tab’s session.
Practice Tip: Open the Network tab in your browser’s developer tools and reload any page. Every request, method, status code and header from this chapter is right there in a real exchange, which makes the protocol far more concrete than the examples alone.
Common Mistake: Expecting a failed HTTP request to reject the fetch promise. A 404 or 500 still resolves successfully — you must check response.ok or response.status yourself.
Important Questions:
- What do the HTTP status code families mean? 2xx success, 3xx redirection, 4xx client error and 5xx server error.
- What is the difference between
localStorageandsessionStorage? Both store string data in the browser, butlocalStoragepersists after the browser is closed whilesessionStorageis cleared when the tab’s session ends.
Chapter 19: Project — A Pixel Art Editor
Part 2: Browser · Difficulty: Hard · Key topics: components, immutable state, dispatch, drawing tools, saving and loading images, undo history
A working pixel art editor with tools, a colour picker, save and load, and undo. Architecturally this is the most valuable chapter in the book, because it builds a small version of the pattern every modern framework uses. The application state is a single object holding the picture, the current tool and the colour. The picture is immutable: drawing produces a new picture rather than changing the old one. Components are objects with a DOM node and a syncState method, and every change goes through one dispatch function. Undo becomes almost free — because pictures are immutable, keeping previous versions is just keeping references. The chapter ends with a section called “Why is this so hard?”, where the author acknowledges that browser technology is both amazing and awkward, and that the awkwardness is why frameworks exist.
Key Points:
- The whole application state is one object; nothing is stored in scattered variables.
- The picture is immutable — drawing returns a new picture rather than modifying the existing one.
- A component is an object with a DOM node and a
syncStatemethod that updates it. - All changes flow through a single
dispatchfunction, so state can never be updated from two places at once. - Undo is nearly free because previous immutable pictures can simply be kept.
- This is the architecture React and similar frameworks use, built here from scratch in plain JavaScript.
Practice Tip: Do the “Keyboard bindings” exercise, which adds shortcuts including ctrl-Z for undo. It makes you touch the component, the dispatch function and the history at once, which is exactly the loop you need to understand before touching React.
Common Mistake: Modifying the picture in place to “make it faster”. The undo history holds references to previous pictures, so mutating one silently corrupts the history.
Important Questions:
- Why is the picture kept immutable? Because previous versions can then be stored simply by keeping references to them, which makes undo history straightforward and prevents accidental shared changes.
- What is a component in this chapter? An object that owns a piece of DOM and provides a
syncStatemethod, called whenever the application state changes so it can update its display.
Chapter 20: Node.js
Part 3: Node · Difficulty: Medium · Key topics: the node command, modules in Node, NPM, the filesystem module, the HTTP module, streams, building a file server
Part 3 moves JavaScript out of the browser. The chapter opens with why Node exists: when a program spends its time reading and writing to the network and disk, how it manages that input and output determines how responsive it is, and asynchronous programming is a good fit. The node command runs a file. Modules come next, with the important practical note that Node started on CommonJS and require and later gained ES modules, so you meet both. NPM is the repository and the command line tool for installing packages. Then the built-in modules that matter: node:fs for reading and writing files, and node:http for running a server — which takes only a few lines. Streams are covered as the idiomatic way to handle data that arrives in pieces. The chapter ends by combining all of it into a file server that gives HTTP access to a directory.
Key Points:
- Node runs JavaScript outside the browser, with the
nodecommand executing a file. - Node began with CommonJS
requireand later added ES modules, so both appear in real projects. - NPM is both the online package repository and the command line tool that installs from it.
node:fsprovides file and directory operations, in callback, promise and synchronous forms.node:httpcan start a working HTTP server in a handful of lines.- A stream delivers data in pieces rather than all at once, which is what makes large files practical.
- Node has no DOM and no
window— browser code does not run here unchanged.
Practice Tip: Install Node and run the few-line HTTP server from the chapter, then visit it in your browser. Serving your own page from your own machine makes the client-server split concrete in a way no diagram does.
Common Mistake: Expecting browser APIs in Node. There is no document, no window and no alert — Node has its own set of modules instead.
Important Questions:
- What is Node.js? A system for running JavaScript outside the browser, originally designed for network tasks, with its own module system and standard library instead of browser APIs.
- What is a stream in Node? An object that delivers or accepts data in pieces rather than all at once, which is what makes handling large files and network responses practical.
Chapter 21: Project — Skill-Sharing Website
Part 3: Node · Difficulty: Hard · Key topics: client-server design, long polling, HTTP interface design, building the server, building the client
The final project ties both halves of the book together: a Node server and a browser client for a meetup where people propose talks. The design section separates responsibilities — the server stores the data and serves the client’s files, the client displays and modifies it. The interesting problem is notifying a client that something changed, since browsers do not accept incoming connections and are usually behind routers that would block them anyway. The solution is long polling: the client makes a request that the server deliberately does not answer until there is something to report. The HTTP interface is designed before either side is written, using JSON bodies and making proper use of HTTP methods, exactly as Chapter 18 recommended. The server then handles requests by method and path, and the client is three files — an HTML page, a stylesheet and a JavaScript file — using the same component pattern as Chapter 19.
Key Points:
- The server stores the data and serves the client’s files; the client displays and modifies that data.
- Browsers do not accept incoming connections, so the server cannot simply push updates.
- Long polling has the client make a request the server holds open until there is something to report.
- The HTTP interface is designed first, before either side is implemented.
- JSON is the format for request and response bodies, with HTTP methods used for their real meanings.
- The client reuses the component and dispatch pattern from Chapter 19.
Practice Tip: Run the finished application in two browser windows side by side and add a talk in one. Watching it appear in the other without a refresh is what makes long polling click, and it takes seconds once the server is running.
Common Mistake: Treating long polling as the modern answer. It is the technique this chapter teaches because it works everywhere with plain HTTP, but production systems today generally use WebSockets or server-sent events.
Important Questions:
- What is long polling? The client sends a request that the server deliberately leaves unanswered until it has something to report, which lets the server push information to a client that cannot accept incoming connections.
- Why can a server not simply open a connection to a browser? Browsers do not accept incoming connections, and clients are usually behind routers that would block such connections anyway.
Download Eloquent JavaScript PDF (Free)
This book is free from its official source. Click below to open the official site, where the 4th edition can be read online with runnable code examples or downloaded as a PDF, a smaller mobile PDF, an EPUB or a MOBI file.
↓ Download PDFHow to Study This Book
Read Part 1 in order and do not skip ahead to the browser chapters, however tempting that is. Chapters 1 to 5 build the language itself, and Chapters 14 onward assume every one of them. If you have programmed before, Chapters 1 to 3 will move quickly; do them anyway, because JavaScript’s bindings, scope and type coercion behave differently from C++ and Java in ways that catch people out.
Chapters 4, 5 and 6 — objects and arrays, higher-order functions, and the secret life of objects — are where the marks are and where most students slow down. Give Chapter 6 two passes: read it once, do the next project chapter, then come back to it.
Do the project chapters rather than reading them. Chapter 7 (the robot) is the first real test of Part 1, and Chapter 12 (the programming language) is the hardest thing in the first half. If you are short of time, Chapter 12 is the one to postpone — but come back to it, because the tree-walking it teaches makes the DOM in Chapter 14 feel familiar.
Chapter 11 on asynchronous programming is the one to read slowly. Promises, async/await and the event loop reappear in Chapters 15, 18, 20 and 21, and every one of those is harder if this chapter was rushed.
For Part 2, read Chapters 13, 14 and 15 together — the browser, the DOM and events are one topic split into three. Chapter 18 on HTTP and forms is the most exam-relevant chapter in the whole book for a Web Development paper.
Chapter 19, the pixel art editor, is the most useful chapter for anyone who will later learn React. It builds the same state-and-components architecture from scratch in plain JavaScript, so frameworks stop looking like magic.
Part 3 needs Node installed on your own machine. If your course does not cover server-side JavaScript, Chapters 20 and 21 can be left until after the exam — but do them eventually, because Chapter 21 is the only place the client and server halves of the book meet.
Use the online edition while learning and the PDF for revision. Every example in the online version runs and can be edited in place, and the exercises have solutions on the site once you have genuinely tried them.
Used In These Programs
This book is used for the Web Development and Web Technologies courses in: BS Computer Science · BS Information Technology. Browse all JavaScript books, all Web Development books, or all Computer Science books.
Who Should Read This
Eloquent JavaScript works for two quite different readers. It assumes no prior programming experience, so a first-semester student can start at Chapter 1 and learn to program with it — though it moves faster and expects more thinking than a typical introductory textbook. It is equally good for a student who already knows C++ or Java and needs JavaScript for a Web Development course, since the early chapters can be read quickly and the real value starts at Chapter 4. The five project chapters are what make it stand out: building a platform game, a pixel art editor and a working client-server application gives you something to show as well as something to answer exam questions from. Chapter 19 in particular is the best free preparation available for later learning React. If you only need the browser side for a specific course, Part 2 can be read on its own after Chapters 1 to 6, though Chapter 11 on asynchronous programming should not be skipped.
Applicable Universities
This book is useful for students at Pakistani universities offering BSCS and BSIT including Punjab University, Virtual University, COMSATS, FAST, UET, NUST, and other HEC-recognized institutions.
FAQs
Is Eloquent JavaScript free?
Yes. The text is free under the Creative Commons Attribution-NonCommercial 3.0 licence, and the complete book can be read online or downloaded as a PDF, EPUB or MOBI. The code examples are separately licensed under an MIT licence, so you may reuse them freely, including in commercial work.
Which edition is this?
The 4th edition, published in 2024. It is the current edition and is actively maintained, with an errata page on the official site.
How many chapters does the book have?
An introduction plus 21 chapters, in three parts: the JavaScript language (Chapters 1–12), the browser (13–19) and Node.js (20–21). Five of those chapters are complete projects.
Is Eloquent JavaScript good for complete beginners?
Yes. It assumes no prior programming experience and starts from values and operators, though it moves faster and expects more thinking than a typical introductory textbook. Students who already know C++ or Java can read the first three chapters quickly.
Does it cover the DOM, events and Node.js?
Yes. The DOM is Chapter 14, events are Chapter 15, HTTP and forms are Chapter 18, and Node.js is covered in Chapters 20 and 21, including building a working HTTP server and a client-server application.
Does it teach React or any other framework?
No, but Chapter 19 builds a pixel art editor using the same state-and-components architecture that React uses, in plain JavaScript. It is the best free preparation available before learning a framework.
Related Books
- Think Python – Allen B. Downey (3rd Edition)
- Problem Solving with Algorithms and Data Structures using Python
- Object Oriented Programming Using C++ – IT Series BSCS
Eloquent JavaScript is the strongest free JavaScript book available, and the 4th edition is current rather than something you have to read around. It teaches the language properly, covers the browser and Node, and gives you five complete projects rather than a list of features. Browse more Computer Science books for the rest of your semester.
Eloquent JavaScript, 4th edition, by Marijn Haverbeke. The text is free under the Creative Commons Attribution-NonCommercial 3.0 licence; the code in the book is separately licensed under an MIT licence. Official source: https://eloquentjavascript.net/