Modern JavaScript Features

JavaScript gets a new edition every year. If you learned it a while ago — or you're reading older tutorials and Stack Overflow answers — this lesson maps out what changed, from the huge ES2015 update to the features added in the last couple of years, so you can recognise and use modern syntax.

ES2015 (ES6): the big one

ES2015 transformed the language. Nearly everything in this course relies on it:

FeatureLesson
let and const05
Arrow functions23
Template literals15
Default and rest parameters, spread, destructuring24, 33
Classes39–40
Modules (import/export)55
Promises52
Map, Set, iterators, generators, for…of, symbols12, 34, 35

2016–2020

JS es2016-2020.js
// ES2016: exponent operator, Array.prototype.includes
console.log(2 ** 10, [1, 2, 3].includes(2));

// ES2017: Object.values/entries, string padding (async/await too)
console.log(Object.entries({ a: 1 }), "5".padStart(3, "0"));

// ES2018: object spread/rest, Promise.finally, named regex groups
const { a, ...rest } = { a: 1, b: 2, c: 3 };
console.log(rest, "2026-10".match(/(?<year>\d{4})/).groups.year);

// ES2019: flat, flatMap, Object.fromEntries, optional catch binding
console.log([[1], [2, [3]]].flat(2), Object.fromEntries([["k", "v"]]));

// ES2020: optional chaining, nullish coalescing, BigInt
const config = { retries: 0 };
console.log(config.server?.port, config.retries ?? 3, 2n ** 64n);
Output
1024 true
[ [ 'a', 1 ] ] 005
{ b: 2, c: 3 } 2026
[ 1, 2, 3 ] { k: 'v' }
undefined 0 18446744073709551616n

2021–2023

JS es2021-2023.js
// ES2021: replaceAll, logical assignment, numeric separators
let title = "a-b-c".replaceAll("-", " ");
let count;
count ??= 1_000_000;
console.log(title, count);

// ES2022: .at(), Object.hasOwn, class fields & #private, top-level await, error cause
console.log([10, 20, 30].at(-1), Object.hasOwn({ x: 1 }, "x"));

class Account {
  #balance = 0;
  static currency = "EUR";
  get balance() { return this.#balance; }
}
console.log(new Account().balance, Account.currency);

// ES2023: findLast, and change-by-copy array methods
const nums = [3, 1, 2];
console.log(nums.findLast(n => n > 1), nums.toSorted(), nums.with(0, 9), nums);
Output
a b c 1000000
30 true
0 EUR
2 [ 1, 2, 3 ] [ 9, 1, 2 ] [ 3, 1, 2 ]

2024–2025

JS es2024-2025.js
// ES2024: Object.groupBy / Map.groupBy, Promise.withResolvers, well-formed strings
const pets = [{ n: "Rex", t: "dog" }, { n: "Tom", t: "cat" }, { n: "Fido", t: "dog" }];
console.log(Object.groupBy(pets, p => p.t).dog.length);

const { promise, resolve } = Promise.withResolvers();
promise.then(v => console.log("resolved later with", v));
resolve("✓");

// ES2025: Set methods, iterator helpers, RegExp.escape, Promise.try
const a = new Set([1, 2, 3]);
console.log([...a.intersection(new Set([2, 3, 4]))]);
console.log([1, 2, 3, 4].values().filter(n => n % 2 === 0).map(n => n * 10).toArray());
Output
2
[ 2, 3 ]
[ 20, 40 ]
resolved later with ✓

Arriving now

  • Temporal — a modern, time-zone-aware replacement for Date (lesson 20), shipping in browsers.
  • Explicit resource management — using declarations that clean up resources automatically at the end of a block.
  • Import attributes — import data from "./data.json" with { type: "json" }.
  • Array.fromAsync — collects an async iterable into an array.

To check whether a feature works in the browsers you support, look it up on caniuse.com or the browser-compatibility tables in MDN Web Docs.

Old patterns and their modern replacements

You might see…Modern equivalent
var self = this;Arrow functions
arr.indexOf(x) !== -1arr.includes(x)
a && a.b && a.b.ca?.b?.c
x !== undefined && x !== null ? x : dx ?? d
Object.assign({}, a, b){ ...a, ...b }
arr[arr.length - 1]arr.at(-1)
JSON.parse(JSON.stringify(obj))structuredClone(obj)
Array.prototype.slice.call(arguments)Rest parameters ...args
Callback pyramidsasync/await
jQuery $(…) for basic DOM workquerySelector, classList, fetch