On this page
Modern JavaScript (ES2020 → ES2026)
Last reviewed 6 Sept 2026
Everything from ES2020 onward is safe to use in evergreen browsers and current Node
without a transpile step. Interviewers increasingly expect familiarity with at least
ES2020–ES2023; Temporal and using are the 2025–26 talking points.
ES2026 — shipping now
The 17th edition. Reached Stage 4 in early 2026.
Temporal API — the Date replacement
Immutable, timezone- and calendar-aware date/time. Fixes almost every Date foot-gun
(mutability, month indexing, parsing, timezone math).
const now = Temporal.Now.plainDateTimeISO(); // 2026-09-06T14:32:10const birthday = Temporal.PlainDate.from('1994-05-20');const daysAlive = now.toPlainDate().since(birthday).days;
const meeting = Temporal.ZonedDateTime.from({ timeZone: 'Asia/Kolkata', year: 2026, month: 9, day: 6, hour: 18,});meeting.withTimeZone('America/New_York').toString(); // same instant, NY wall-clockExplicit resource management — using / await using
Deterministic cleanup, no try/finally. The resource must implement
Symbol.dispose (or Symbol.asyncDispose).
function openFile(path) { const handle = fs.openSync(path, 'r'); return { handle, [Symbol.dispose]() { fs.closeSync(handle); } };}
{ using file = openFile('data.txt'); read(file.handle);} // file automatically closed here, even on throwArray.fromAsync()
Builds an array from an async iterable — the for await loop you kept writing by hand.
const lines = await Array.fromAsync(readLinesStream(file));Error.isError()
Reliable cross-realm error check (survives iframes / worker boundaries where
instanceof Error fails).
if (Error.isError(err)) console.error(err.stack);ES2025
16th edition, approved June 2025.
Iterator helpers
Lazy .map / .filter / .take / .drop / .flatMap / .reduce directly on any
iterator — no [...spread] to an array first.
const firstThreeEven = someHugeIterator() .filter((n) => n % 2 === 0) .map((n) => n * n) .take(3) .toArray();Set methods
const a = new Set([1, 2, 3]);const b = new Set([2, 3, 4]);a.union(b); // {1,2,3,4}a.intersection(b); // {2,3}a.difference(b); // {1}a.isSubsetOf(b); // falsePromise.try()
Runs a function and always returns a promise — sync throws, sync returns, and async
results all funnel through one .catch.
Promise.try(() => mightThrowSyncOrReturnPromise(input)) .then(handle) .catch(reportError);JSON modules
import config from './config.json' with { type: 'json' };RegExp.escape()
Escape a string for safe use inside a dynamic RegExp.
const re = new RegExp(RegExp.escape(userInput), 'g');Also in ES2025
Float16Array— 16-bit float typed array (ML, graphics, WebGPU).- Regex pattern modifiers —
(?i:...)/(?-i:...)to toggle flags for part of a pattern. - Duplicate named capture groups across alternation branches.
ES2024
Object.groupBy() / Map.groupBy()
const byRole = Object.groupBy(users, (u) => u.role);// { admin: [...], member: [...] }Promise.withResolvers()
Get a promise plus its resolve / reject without the callback-closure dance.
const { promise, resolve, reject } = Promise.withResolvers();socket.once('message', resolve);socket.once('error', reject);await promise;Also in ES2024
String.prototype.isWellFormed()/toWellFormed()— detect / fix lone surrogates.- RegExp
vflag — set notation and string properties in character classes. ArrayBuffer.prototype.resize()/transfer()— resizable and transferable buffers.Atomics.waitAsync()— non-blocking wait on shared memory.
ES2023
Change array by copy
Non-mutating versions of the four mutating array methods:
const sorted = list.toSorted((a, b) => a - b); // list unchangedconst reversed = list.toReversed();const patched = list.with(2, 'x'); // copy with index 2 replacedconst spliced = list.toSpliced(1, 2, 'a', 'b');findLast() / findLastIndex()
const lastError = log.findLast((e) => e.level === 'error');Also in ES2023
- Symbols allowed as WeakMap keys.
- Hashbang (
#!/usr/bin/env node) is now part of the grammar.
ES2022
- Top-level
awaitin modules — no async IIFE wrapper for entry points. - Class fields,
#privatefields and methods,static {}init blocks. Array.prototype.at()/String.prototype.at()—arr.at(-1)for the last item.Object.hasOwn(obj, key)— the safe replacement forobj.hasOwnProperty(key).error.cause—new Error('failed', { cause: originalError }).- RegExp
dflag — match start/end indices viamatch.indices.
class Counter { #count = 0; // private field static #instances = 0; static { Counter.#instances = 0; } // static block increment() { return ++this.#count; }}ES2021
String.prototype.replaceAll()— no more/gregex just to replace all.Promise.any()— first fulfilled wins; rejects with anAggregateErroronly if all fail.- Logical assignment —
a ??= b,a ||= b,a &&= b. - Numeric separators —
1_000_000,0xFF_EC_DE. WeakRef/FinalizationRegistry— advanced, rarely needed.
ES2020
The baseline for “modern JS”.
- Optional chaining —
user?.profile?.avatar. - Nullish coalescing —
value ?? fallback(onlynull/undefined, unlike||). Promise.allSettled()— wait for all, never short-circuit on rejection.BigInt— arbitrary-precision integers:9007199254740993n.globalThis— one name for the global object across browser / Node / worker.- Dynamic
import()—const mod = await import('./heavy.js'). String.prototype.matchAll(),import.meta, stableArray.prototype.sort.
Proposals to watch
| Proposal | Stage | What it is |
|---|---|---|
| Decorators | 3 | @decorator syntax for classes, fields, methods, accessors |
Signals (TC39/proposal-signals) | 1 | Built-in reactive state primitive (Signal.State, Signal.Computed) |
Math.sumPrecise | 3+ | Accurate summation of a list of numbers |
Pattern matching (match / when) | 1 | Structural switch-like matching |
| Records & Tuples | on hold | Deeply immutable value types (#{} / #[]) — being redesigned |
Check the current list at github.com/tc39/proposals.