Chapter 1 · Foundations Revisited

Modern JavaScript & ECMAScript

You can already write JavaScript. This chapter gives you the map: how the language is versioned, what ES6 changed, where your code runs, and the two modes (scripts and modules) that quietly change the rules.

You will learn

  • The difference between JavaScript, ECMAScript, engines and runtimes.
  • How a feature becomes part of the language (TC39 stages) and the ES5 → ES2026 timeline.
  • ES6 (ES2015) at a glance: the release that changed how JavaScript is written.
  • How to run code in Node.js and in the browser console.
  • Strict mode, and how scripts differ from modules.

JavaScript, ECMAScript, engines and runtimes

Four words get mixed up all the time. Keeping them apart explains a lot of "why does this work here but not there?" moments.

WordMeaningExamples
ECMAScriptThe language specification (ECMA-262): syntax, types, built-in objects like Array and PromiseES5, ES2015 (ES6), ES2025
JavaScriptThe everyday name for ECMAScript plus whatever the host adds—
EngineThe program that parses and runs the languageV8 (Chrome, Node), SpiderMonkey (Firefox), JavaScriptCore (Safari)
Runtime / hostAn engine plus extra APIs for its environmentBrowsers add document, fetch; Node adds fs, process; also Deno and Bun

So Array.prototype.map is language and works everywhere, while document.querySelector is a browser API and doesn't exist in Node. This book teaches the language and uses Node 26 to run it. When an example needs a browser, it says so.

☕ Java: despite the name, JavaScript is not related to Java. The syntax looks similar (braces, if, for), but JavaScript is dynamically typed, uses prototypes instead of class-based inheritance under the hood, and has first-class functions. The name was a 1995 marketing decision.

How the language evolves

A committee called TC39 designs new features. Each proposal moves through numbered stages: 0 (idea), 1 (proposal), 2 (draft), 2.7 (approved in principle, needs tests), 3 (ready to implement), and 4 (finished). Everything that reaches stage 4 by early in the year goes into that year's edition. Engines often ship a feature at stage 3, before it's officially "in" the standard.

EditionYearHighlights
ES31999regular expressions, try/catch
ES52009strict mode, JSON, Array.prototype.forEach/map/filter, Object.defineProperty
ES6 / ES20152015the big one: let/const, arrows, classes, modules, promises, template literals, destructuring, spread/rest, default parameters, Map/Set, Symbol, iterators and generators, for…of, Proxy/Reflect
ES20162016**, Array.prototype.includes
ES20172017async/await, Object.entries/values, padStart
ES20182018object rest/spread, async iteration, regex named groups and lookbehind
ES20192019flat/flatMap, Object.fromEntries, optional catch binding
ES20202020?., ??, BigInt, Promise.allSettled, dynamic import(), globalThis
ES20212021??= ||= &&=, replaceAll, Promise.any, numeric separators 1_000, WeakRef
ES20222022class fields and #private, static blocks, top-level await, .at(), Error cause, Object.hasOwn
ES20232023toSorted/toReversed/toSpliced/with, findLast
ES20242024Object.groupBy, Promise.withResolvers, regex v flag
ES20252025iterator helpers, Set methods (union, …), Promise.try, RegExp.escape, JSON import attributes, Float16Array
ES20262026(finalised or shipping in engines during 2025–26) using / DisposableStack (resource management), Error.isError, Array.fromAsync

Throughout the book, a badge like ES2015 or ES2023 shows when a feature arrived. Blue badges mark ES6 features; the yellow ones are later.

📝 Why "ES6" and "ES2015" both exist: the sixth edition was released in 2015. After it, TC39 switched to yearly releases named after the year, so there is no "ES7" in official use, though people still say it.

ES6 at a glance ES2015

If your JavaScript habits were formed before 2015, or by tutorials that copied that style, this table is your upgrade path. Each row is covered in depth in the chapter listed.

FeatureBefore (ES5)After (ES6+)Chapter
Block-scoped variablesvar x = 1;let x = 1; const y = 2;2
Arrow functionsfunction (x) { return x * 2; }x => x * 24
Default and rest parametersb = b || 1; arguments(a, b = 1, ...rest) => {}4
Template literals"Hi " + name + "!"`Hi ${name}!`6
Destructuringvar a = obj.a, b = obj.b;const { a, b } = obj;5
Spreadarr1.concat(arr2)[...arr1, ...arr2]5
Shorthand object literals{ name: name, f: function () {} }{ name, f() {} }10
for…offor (var i = 0; i < a.length; i++)for (const x of a)3
Classesconstructor functions + prototypeclass A extends B {}11
Promisesnested callbacksfetchX().then(…)12
Iterators and generatorshand-written index loopsfunction* gen() { yield 1; }13
ModulesIIFEs, global variables, CommonJSimport / export14
Map, Set, WeakMapplain objects as mapsnew Map()9
Symbol, Proxy, Reflect—metaprogramming15

Running JavaScript

In Node.js

Node.js runs JavaScript outside the browser. This book uses Node 26. Check your version:

Typing node alone opens the REPL (read–eval–print loop). Type an expression and Node prints its value:

📝 About REPL blocks in this book: lines starting with > are what you type, and the lines below are Node's answer. The real REPL also prints undefined after statements like const x = 1; the book leaves those out to reduce noise. Appendix B covers the REPL in detail.

To run a file, pass its name:

In the browser

Every browser has a JavaScript console in its developer tools: press F12, or Cmd+Option+J on a Mac in Chrome. It works like the Node REPL, but with browser APIs such as document available. In a web page, you load code with a <script> tag:

Strict mode

ES5 introduced strict mode, an opt-in variant of the language that turns silent mistakes into errors and removes a few confusing features. You turn it on by putting the string "use strict"; at the very top of a script or a function.

SituationSloppy modeStrict mode
Assigning to an undeclared variablesilently creates a globalReferenceError
this in a plain function callthe global objectundefined
Writing to a read-only propertysilently ignoredTypeError
Duplicate parameter names function f(a, a)allowedSyntaxError
with statement, octal literals like 010allowedSyntaxError

💡 Good news: you rarely write "use strict" yourself today. Modules and class bodies are always strict. Since this book uses modules everywhere, you get strict mode for free.

Scripts vs modules

The same code can behave differently depending on whether it's loaded as a classic script or as a module (an ES module, or ESM, covered fully in Chapter 14).

Classic scriptModule
Strict modeoff unless "use strict"always on
Top-level variablesshared between all scripts (global)private to the module
Top-level thisthe global object (browser) / module.exports (Node CJS)undefined
import / exportnot allowed (only dynamic import())allowed
Top-level awaitnoyes ES2022
In Node.cjs, or .js without "type": "module".mjs, or .js with "type": "module" in package.json

⚠️ Pitfall: copy a snippet that uses import into a plain .js file, run it in a folder with no package.json, and older Node versions throw SyntaxError: Cannot use import statement outside a module. Node 22+ detects ESM syntax automatically, but the clear fix is to use .mjs, or to add "type": "module" to package.json, as the book's playground does.

More examples

1. Feature detection instead of version checks

2. ES5 vs ES6 in one small function

3. Which runtime am I in?

4. A tiny module pair

5. The global object, everywhere

globalThis ES2020 is the standard name for the global object. Before it, you needed window in browsers, global in Node, and self in workers.

Recap

  • ECMAScript is the spec, engines run it, and runtimes (browsers, Node) add host APIs.
  • TC39 ships a new edition every year. ES6/ES2015 was the big modernisation.
  • Run code with the Node REPL, node file.js, or the browser console.
  • Strict mode turns silent bugs into errors, and modules and classes are always strict.
  • Modules have private top-level scope, this === undefined at the top, import/export and top-level await.

Exercises

Exercise 1 🟢 easy explain why

Why does document.title fail with ReferenceError: document is not defined in Node, while [1, 2].includes(2) works in both Node and the browser?

Show solution

document is a browser host API, not part of ECMAScript, so Node doesn't provide it. Array.prototype.includes is part of the language itself (ES2016), so every modern engine has it.

Exercise 2 🟢 easy build it · playground

In playground/, create hello.js that prints the Node major version and the current year, like Node 26 in 2026. Run it with node hello.js.

Show solution

process is a Node host object, and the template literal (Chapter 6) inserts the values.

Exercise 3 🟢 easy predict the output

Which edition introduced each feature? let, async/await, ??, Object.groupBy

Show solution

let: ES2015. async/await: ES2017. ??: ES2020. Object.groupBy: ES2024.

Exercise 4 🟢 easy fix the bug

This classic script has a typo that strict mode would catch. Add strict mode and say what changes.

Show solution

With "use strict" and the original typo, the call throws ReferenceError: quantiy is not defined instead of quietly creating a global. Declaring the variable fixes it.

Exercise 5 🟡 medium predict the output

What does this print when saved as a.mjs and run with node a.mjs?

Show solution

undefined undefined. At the top of a module, this is undefined, and even var at module level doesn't create a property on the global object, because module scope is private.

Exercise 6 🟡 medium refactor it

Rewrite this ES5 function in modern style: default parameter, arrow function, template literal and const.

Show solution

A default parameter only applies when the argument is undefined. The ES5 || trick also replaced "", which could be a bug.

Exercise 7 🟡 medium build it · playground

Create playground/features.js that checks for Object.groupBy, Set.prototype.union, Iterator.prototype.map, Promise.try and DisposableStack, and prints a ✓ or ✗ line for each.

Show solution

Each check is a function, so a missing global like Iterator can't crash the script before the check runs: ?. stops early instead.

Exercise 8 🟡 medium explain why

A teammate says: "This feature is stage 3, so it's part of ES2025." Is that right?

Show solution

No. Stage 3 means the design is finished and engines may ship it, but it only joins an edition after reaching stage 4 (two shipping implementations and tests). Many stage-3 features are already usable in Node or browsers, but they're not "in" the standard yet.

Exercise 9 🔴 hard predict the output

Same code, two files. What does each print?

Show solution

As t.cjs (sloppy mode) it prints object: a plain call gets the global object as this. As t.mjs (a module, so strict) it prints undefined. Chapter 4 explains this fully.

Exercise 10 🔴 hard mini-project · playground

Create playground/env-report.js, a module that prints a small report: the Node version, whether the file runs as a module (typeof this === "undefined" at the top), whether top-level await works, and a sorted list of three ES2023+ features that are available. Use template literals, const, arrows and Object.entries.

Show solution

It combines this chapter's ideas: module facts (this, top-level await), feature detection, and a first taste of ES6+ syntax that later chapters explain.

Official docs