JavaScript is the language; ECMAScript is the standard that defines it. Each edition of the standard (ECMA-262) is a “JavaScript version”. Since ES2015 a new edition is approved every June, and the latest finished one is ECMAScript 2026 (the 17th edition, June 2026). Names like “ES6” and “ES7” are older numbering for ES2015 and ES2016, so ES7 was released in June 2016.
The sections below list every edition, when it came out, and what a working developer should know about it. The code examples were run on Node.js 22, so features newer than that are shown without output.
ECMAScript editions at a glance
Dates are the editions listed on ecma-international.org.
| Edition | Name | Released | Headline additions |
|---|---|---|---|
| 1 | ES1 | June 1997 | First standard |
| 2 | ES2 | 1998 | Editorial alignment with ISO/IEC 16262 |
| 3 | ES3 | December 1999 | Regular expressions, try/catch, do-while, switch |
| 4 | ES4 | never published | Abandoned |
| 5 / 5.1 | ES5 | December 2009 / June 2011 | Strict mode, JSON, array methods |
| 6 | ES2015 (ES6) | June 2015 | let/const, classes, modules, arrow functions, promises |
| 7 | ES2016 (ES7) | June 2016 | **, Array.prototype.includes |
| 8 | ES2017 | June 2017 | async/await, Object.entries |
| 9 | ES2018 | June 2018 | Object rest/spread, async iteration, Promise.finally |
| 10 | ES2019 | June 2019 | flat, flatMap, Object.fromEntries |
| 11 | ES2020 | June 2020 | ?., ??, BigInt, Promise.allSettled |
| 12 | ES2021 | June 2021 | replaceAll, ??=, Promise.any |
| 13 | ES2022 | June 2022 | Class fields, private fields, top-level await, at() |
| 14 | ES2023 | June 2023 | toSorted, findLast, hashbang |
| 15 | ES2024 | June 2024 | Object.groupBy, Promise.withResolvers |
| 16 | ES2025 | June 2025 | Set methods, iterator helpers, Promise.try |
| 17 | ES2026 | June 2026 | Array.fromAsync, Error.isError, Math.sumPrecise |
JavaScript vs ECMAScript
JavaScript was created at Netscape in 1995. It was handed to Ecma International for standardization, and because “JavaScript” was a trademark, the standard got the name ECMAScript. Browsers and runtimes (V8 in Chrome and Node.js, SpiderMonkey in Firefox, JavaScriptCore in Safari) are separate implementations that ship a feature on their own schedule.
Two things follow from that. A feature is “in JavaScript” for practical purposes only when the engines you target support it, so check the compatibility tables on MDN. And a proposal becomes part of an edition only when it reaches stage 4 in TC39, the committee that maintains the language. Proposals that are still at earlier stages (decorators, pattern matching) are not part of any edition.
How the naming changed
Editions 1 to 6 were numbered. ES4 was abandoned, which is why ES5 follows ES3. With the 6th edition the committee switched to a yearly release and named editions by year, so ES6 became ES2015, ES7 became ES2016, and so on. Both names are still in use, but the year form is the official one. When someone says “ES7”, they mean ES2016, which is a small release with two features.
ES1 to ES3 (1997 to 1999)
ES1 defined the basic syntax, types and core objects such as Array, Date and Math. ES2 was an editorial update. ES3 added regular expressions, try/catch, do-while and switch. For years, ES3 was what every browser could be trusted to run.
ES5 (2009, revised 2011)
ES5 arrived after a decade without a new edition. It added strict mode ("use strict"), the built-in JSON object, the array methods forEach, map, filter, reduce, some and every, Object.keys, Object.create, Object.freeze and Function.prototype.bind. It is the baseline that legacy code and old browsers still target.
ES2015 (ES6): the big one
ES2015 changed how JavaScript is written. It added let and const, arrow functions, classes, template literals, destructuring, default and rest parameters, the spread operator, Promise, Map and Set, Symbol, iterators and generators, and the import/export module syntax. If you only learn one edition well, learn this one. The lesson on constants covers the const part.
const greet = name => `Hello, ${name}!`;
const { name, ...rest } = { name: "Alice", age: 30, city: "Oslo" };
console.log(greet("Alice"), name, rest);
// Hello, Alice! Alice { age: 30, city: 'Oslo' }
(The object form of ...rest used here arrived later, in ES2018; array rest and spread are ES2015.)
ES2016 (ES7): two features
The exponentiation operator ** and Array.prototype.includes(). That is the whole edition, which is why “ES7 features” searches turn up so little.
console.log(2 ** 3, [1, 2, 3].includes(2));
// 8 true
ES2017 (ES8): async/await
async/await lets you write promise-based code in a straight line. The edition also added Object.values(), Object.entries(), padStart(), padEnd(), Object.getOwnPropertyDescriptors(), trailing commas in function parameter lists, and SharedArrayBuffer/Atomics.
console.log(Object.entries({ a: 1, b: 2 }), "7".padStart(3, "0"));
// [ [ 'a', 1 ], [ 'b', 2 ] ] 007
For how promises and the event loop actually run this code, see the JavaScript Promise guide and the event loop guide.
ES2018 (ES9)
Rest and spread properties for objects, asynchronous iteration (for await...of), Promise.prototype.finally(), and regex improvements: named capture groups, lookbehind, the s (dotAll) flag and Unicode property escapes.
ES2019 (ES10)
Array.prototype.flat() and flatMap(), Object.fromEntries(), trimStart() and trimEnd(), optional catch binding (catch { } without a variable), and a requirement that Array.prototype.sort be stable.
console.log([1, [2, [3, [4]]]].flat(Infinity), Object.fromEntries([["a", 1]]));
// [ 1, 2, 3, 4 ] { a: 1 }
ES2020 (ES11)
Optional chaining ?., nullish coalescing ??, BigInt, Promise.allSettled(), globalThis, dynamic import() and String.prototype.matchAll(). The difference between ?? and || matters: ?? only falls back on null and undefined, so 0 and "" are kept.
console.log(null ?? "default", 0 ?? 42, ({ a: {} }).a?.b?.c, 2n ** 64n);
// default 0 undefined 18446744073709551616n
ES2021 (ES12)
Logical assignment operators (||=, &&=, ??=), numeric separators, String.prototype.replaceAll(), Promise.any(), and WeakRef with FinalizationRegistry.
let a = null;
a ??= 10;
console.log(a, "a-b-c".replaceAll("-", "_"), 1_000_000);
// 10 a_b_c 1000000
ES2022 (ES13)
Class fields, private fields and methods (#name), static class fields and static blocks, top-level await in modules, Array.prototype.at(), Object.hasOwn(), the cause option for errors, and the regex d flag (match indices).
class Counter {
#n = 0;
static label = "counter";
inc() { this.#n++; return this.#n; }
}
console.log(new Counter().inc(), Counter.label, [1, 2, 3].at(-1), Object.hasOwn({ x: 1 }, "x"));
// 1 counter 3 true
const err = new Error("outer", { cause: new Error("inner") });
console.log(err.cause.message);
// inner
Top-level await only works in ES modules (a .mjs file, or "type": "module" in package.json):
const data = await Promise.resolve({ ok: true });
console.log(data);
// { ok: true }
ES2023 (14th edition)
The main addition is the “change array by copy” group: toSorted(), toReversed(), toSpliced() and with(). They return a new array instead of mutating the original, which is what you want in React state or any code that avoids shared mutation. The edition also added findLast() and findLastIndex(), hashbang (#!) comment syntax, and symbols as WeakMap keys.
const nums = [3, 1, 2];
console.log(nums.toSorted(), nums, nums.toReversed(), nums.with(0, 9), nums.findLast(n => n > 1));
// [ 1, 2, 3 ] [ 3, 1, 2 ] [ 2, 1, 3 ] [ 9, 1, 2 ] 2
ES2024 (15th edition)
Grouping with Object.groupBy() and Map.groupBy(), Promise.withResolvers(), resizable and transferable ArrayBuffers, Atomics.waitAsync(), isWellFormed() / toWellFormed() on strings, and the regex v flag.
const people = [{ n: "A", age: 30 }, { n: "B", age: 25 }, { n: "C", age: 30 }];
console.log(Object.groupBy(people, p => p.age));
// [Object: null prototype] {
// '25': [ { n: 'B', age: 25 } ],
// '30': [ { n: 'A', age: 30 }, { n: 'C', age: 30 } ]
// }
console.log("ab\uD800".isWellFormed());
// false
Object.groupBy returns an object with a null prototype, so it has no inherited keys like toString to collide with your group names.
ES2025 (16th edition)
New Set methods (union, intersection, difference, symmetricDifference, isSubsetOf and friends), iterator helpers (map, filter, take, drop, toArray and more on iterators), Promise.try(), RegExp.escape(), regex modifiers and duplicate named capture groups, JSON modules with import attributes, and Float16Array.
console.log(new Set([1, 2, 3]).union(new Set([3, 4])));
// Set(4) { 1, 2, 3, 4 }
console.log([1, 2, 3, 4].values().filter(n => n % 2 === 0).map(n => n * 10).toArray());
// [ 20, 40 ]
Iterator helpers are lazy: they process one item at a time instead of building intermediate arrays, which helps with large or infinite sequences.
ES2026 (17th edition)
The edition approved in June 2026 adds, among other things:
Array.fromAsync(): build an array from an async iterable.Error.isError(): reliably tell whether a value is an error, including errors created in another iframe or VM context, whereinstanceof Errorfails.Math.sumPrecise(): sum numbers without accumulating floating-point error.Iterator.concat(): chain several iterators into one.Uint8Arrayconversions to and from Base64 and hex (toBase64(),fromBase64(),toHex(),fromHex()).JSON.rawJSON()andJSON.isRawJSON(), plus access to the source text inJSON.parserevivers, which lets you keep large integers exact.getOrInsert()andgetOrInsertComputed()onMapandWeakMap.
const rows = await Array.fromAsync((async function* () { yield 1; yield 2; })());
console.log(rows);
// [ 1, 2 ]
The list above was checked against the text of the ECMAScript 2026 specification at 262.ecma-international.org/17.0, and ran Array.fromAsync on Node.js 22. Runtime support for the rest varies, so check MDN’s compatibility tables before using them without a fallback.
What comes next
The TC39 list of finished proposals shows what is already stage 4 and waiting for the next edition. As of October 2026 it includes Temporal (the modern date and time API), explicit resource management (using declarations) and an Iterator join/chunking family, expected in ES2027. Decorators, pattern matching and records and tuples are not finished, and the pipeline operator is still a proposal. Treat anything not on the finished list as something that may still change.
Which version should you target?
- Learning: learn ES2015 to ES2022 first. That covers nearly all code you will read:
let/const, arrow functions, modules, promises and async/await, destructuring,?.and??, classes. - Browser apps: modern browsers update automatically, so ES2022-level syntax is generally safe. For newer library methods, check MDN or caniuse for the browsers your users have. Bundlers such as Vite, esbuild and Babel can compile newer syntax down, but they cannot add missing built-ins unless you include a polyfill.
- Node.js: check your Node version, since each release ships a specific V8 version. Features like
Promise.tryandRegExp.escapewere not in Node 22, for example (Promise.trythrows “is not a function” on Node 22.22). - Interviews: use ES2015+ syntax freely; interviewers expect
const, arrow functions,Map/Set, and destructuring. If you want to practice them on real problems, AlgoCademy has JavaScript lessons with step-by-step feedback.
Common mistakes
- Calling ES6 “the latest version”. It was released in 2015; the latest edition is ES2026.
- Using a method that the runtime doesn’t have. Syntax errors show up at load time, but a missing method such as
Promise.tryfails only when the line runs. - Expecting
sortandreverseto be pure. They mutate the array. UsetoSorted()andtoReversed()(ES2023) when you need a copy. - Using
||for defaults.0 || 42is42;0 ?? 42is0.
