JavaScript course menu
Modern JavaScript Features: ES2015 to ES2025 Highlights
A tour of modern JavaScript features and when they arrived: let and const, arrow functions, template literals, destructuring, classes, modules, promises, async/await, optional chaining, nullish coalescing, private fields, at(), Object.groupBy, structuredClone and more, with how to check browser support.
On this page
JavaScript is standardised as ECMAScript, and since 2015 a new edition has been published every June. This lesson collects the most useful features of the modern language in one place, many of which you have already used in this course, and shows a few small additions you may not have met.
ES2015 (ES6): the big update#
ES2016 to ES2019#
console.log(2 ** 10); // ES2016: exponent operator
console.log(["a", "b"].includes("b")); // ES2016: includes
console.log(Object.entries({ x: 1, y: 2 })); // ES2017: Object.entries
console.log("5".padStart(3, "0")); // ES2017: padStart
console.log({ ...{ a: 1 }, b: 2 }); // ES2018: object spread
console.log([1, [2, [3]]].flat(Infinity)); // ES2019: flat
console.log(Object.fromEntries([["k", "v"]])); // ES2019: fromEntries1024
true
[ [ 'x', 1 ], [ 'y', 2 ] ]
005
{ a: 1, b: 2 }
[ 1, 2, 3 ]
{ k: 'v' }ES2017 also added async/await (async and await).
ES2020#
const user = { profile: null, settings: { volume: 0 } };
console.log(user.profile?.photo); // optional chaining
console.log(user.settings.volume ?? 50); // nullish coalescing
console.log(2n ** 64n); // BigInt
const results = await Promise.allSettled([Promise.resolve(1), Promise.reject(new Error("x"))]);
console.log(results.map((r) => r.status));undefined
0
18446744073709551616n
[ 'fulfilled', 'rejected' ]ES2020 also standardised globalThis (the global object in every environment) and dynamic import().
ES2021 and ES2022#
console.log("a-b-c".replaceAll("-", "+")); // ES2021: replaceAll
console.log(1_000_000 + 1); // ES2021: numeric separators
let config = { retries: 0 };
config.retries ||= 3; // ES2021: logical assignment
config.timeout ??= 5000;
console.log(config);
console.log([10, 20, 30].at(-1)); // ES2022: at()
console.log(Object.hasOwn({ a: 1 }, "a")); // ES2022: Object.hasOwn
class Counter {
#count = 0; // ES2022: private fields
static create() { return new Counter(); } // static methods (ES2015) and static fields (ES2022)
increment() { return ++this.#count; }
}
console.log(Counter.create().increment());a+b+c
1000001
{ retries: 3, timeout: 5000 }
30
true
1ES2022 also added top-level await in modules and the cause option for errors (Error Handling).
ES2023 to ES2025#
const scores = [72, 95, 58];
console.log(scores.toSorted((a, b) => b - a), scores); // ES2023: non-mutating
console.log(scores.findLast((s) => s > 60)); // ES2023: findLast
console.log(scores.with(0, 100)); // ES2023: with
const people = [
{ name: "Asha", city: "Pune" },
{ name: "Ravi", city: "Delhi" },
{ name: "Meera", city: "Pune" },
];
console.log(Object.groupBy(people, (p) => p.city)); // ES2024: groupBy
const a = new Set([1, 2, 3]);
const b = new Set([2, 3, 4]);
console.log(a.intersection(b), a.union(b).size); // ES2025: Set methods[ 95, 72, 58 ] [ 72, 95, 58 ]
95
[ 100, 95, 58 ]
[Object: null prototype] {
Pune: [ { name: 'Asha', city: 'Pune' }, { name: 'Meera', city: 'Pune' } ],
Delhi: [ { name: 'Ravi', city: 'Delhi' } ]
}
Set(2) { 2, 3 } 4(Object.groupBy returns an object without a prototype, which Node.js labels [Object: null prototype]; you use it like any other object.)
Also useful, though not part of ECMAScript itself, is structuredClone(value), provided by browsers and Node.js for deep copies.
Checking support#
Modern browsers update automatically, so features from ES2015 to ES2022 work everywhere current. For the newest features:
- MDN Web Docs has a browser compatibility table at the bottom of every feature page.
- caniuse.com shows support across browsers and versions.
- Build tools such as Babel, esbuild and TypeScript can transpile newer syntax into older syntax for older browsers. Missing built-in functions can be added with polyfills.
- In Node.js, check the version (
node --version) and the Node.js release notes.
Practice#
Practice exercise
Modernise this older code using const/let, an arrow function, template literals, destructuring, ?? and toSorted:
var users = [{ name: "Ravi", age: 31 }, { name: "Asha" }, { name: "Meera", age: 28 }];
var sorted = users.slice().sort(function (a, b) { return a.name < b.name ? -1 : 1; });
for (var i = 0; i < sorted.length; i++) {
var age = sorted[i].age !== undefined && sorted[i].age !== null ? sorted[i].age : "unknown";
console.log(sorted[i].name + " (" + age + ")");
}Show solution
const users = [{ name: "Ravi", age: 31 }, { name: "Asha" }, { name: "Meera", age: 28 }];
const sorted = users.toSorted((a, b) => a.name.localeCompare(b.name));
for (const { name, age } of sorted) {
console.log(`${name} (${age ?? "unknown"})`);
}Asha (unknown)
Meera (28)
Ravi (31)Key takeaway#
Finished this lesson?