What arrived in each edition since the ES2015 revolution — a map for reading any codebase and for “what's new in JavaScript?” questions.
| Edition | Headline features |
|---|---|
| ES2015 (ES6) | let/const, arrows, classes, template literals, destructuring, default/rest/spread, modules, Promise, Map/Set/WeakMap, Symbol, iterators, generators, for…of, Proxy/Reflect |
| ES2016 | ** exponent, Array.prototype.includes |
| ES2017 | async/await, Object.values/entries, padStart/padEnd, Object.getOwnPropertyDescriptors, SharedArrayBuffer/Atomics |
| ES2018 | Object rest/spread, async iteration (for await), Promise.finally, regex named groups, lookbehind, s flag |
| ES2019 | flat/flatMap, Object.fromEntries, trimStart/trimEnd, optional catch binding, stable sort |
| ES2020 | ?., ??, BigInt, Promise.allSettled, globalThis, dynamic import(), matchAll |
| ES2021 | &&= ||= ??=, replaceAll, Promise.any, numeric separators 1_000, WeakRef |
| ES2022 | Class fields, #private, static blocks, top-level await, .at(), Object.hasOwn, Error cause, #x in obj |
| ES2023 | toSorted/toReversed/toSpliced/with, findLast/findLastIndex, hashbang, symbols as WeakMap keys |
| ES2024 | Object.groupBy/Map.groupBy, Promise.withResolvers, Array.fromAsync, regex v flag, resizable ArrayBuffer, String.isWellFormed |
| ES2025 | Iterator helpers, new Set methods (union, intersection…), RegExp.escape, Promise.try, JSON modules / import attributes, Float16Array |
A tour in one program
const users = [
{ name: "Asha", team: "web", profile: { city: "Pune" }, score: 72 },
{ name: "Ravi", team: "api", profile: null, score: 91 },
{ name: "Mei", team: "web", profile: { city: "Delhi" }, score: 85 },
];
// ES2020 optional chaining + nullish coalescing
console.log(users.map((u) => u.profile?.city ?? "unknown"));
// ES2023 non-mutating sort + ES2022 at()
console.log(users.toSorted((a, b) => b.score - a.score).at(0).name);
// ES2024 groupBy
const byTeam = Object.groupBy(users, (u) => u.team);
console.log(Object.entries(byTeam).map(([t, list]) => `${t}:${list.length}`));
// ES2021 numeric separators + logical assignment
const config = { limit: null };
config.limit ??= 1_000_000;
console.log(config.limit);
// ES2025 Promise.try + Set methods
const result = await Promise.try(() => JSON.parse('{"ok":true}'));
console.log(result, new Set(["a", "b"]).union(new Set(["c"])).size);
// ES2022 top-level await, error cause
const err = new Error("outer", { cause: new Error("inner") });
console.log(err.cause.message);[ 'Pune', 'unknown', 'Delhi' ]
Ravi
[ 'web:2', 'api:1' ]
1000000
{ ok: true } 3
innerOn the horizon
| Proposal | What it adds |
|---|---|
| Temporal | Modern immutable date/time API replacing Date |
| Explicit resource management | using res = getResource() — automatic cleanup via Symbol.dispose |
| Decorators | @logged method() {} — standardised class decorators (used by TypeScript/Angular) |
| Import defer / source phase imports | Lazier module evaluation, WebAssembly integration |
How features arrive: the TC39 process
Stage 0–1
Idea and proposal with a champion on the TC39 committee.
Stage 2 / 2.7
Draft spec text chosen; 2.7 = approved in principle, awaiting tests.
Stage 3
Recommended for implementation; engines ship it (often behind flags).
Stage 4
Two shipping implementations + tests → included in the next yearly edition.
What were the most important ES6 features?