JavaScript ES2024+ Features Worth Using (and What to Skip)

Link copied
JavaScript ES2024+ Features Worth Using (and What to Skip)

JavaScript ES2024+ Features Worth Using (and What to Skip)

JS Tutorial Module 6: Modern JS Lesson 6.2

JavaScript ships new language features every year. Most are small, some are transformative, and a few are answer-existing-pain wins you should adopt today. This article walks through the ES2023, ES2024, and ES2025 additions worth knowing — what to use immediately, what to wait on, and what to skip.

This is Part 18 — the final part of the JavaScript Fundamentals series. If you've made it through the previous 17, you're now better equipped than 90% of working JS developers to read, debug, and architect modern code.

ES2023 highlights (universally supported) #

Non-mutating array methods #

Before ES2023, arr.sort(), arr.reverse(), and arr.splice() all mutated the array. ES2023 added immutable equivalents:

const arr = [3, 1, 2];
const sorted   = arr.toSorted();          // [1, 2, 3] — arr unchanged
const reversed = arr.toReversed();         // [2, 1, 3] — arr unchanged
const spliced  = arr.toSpliced(1, 1);      // [3, 2]
const replaced = arr.with(0, 99);          // [99, 1, 2] — replace index 0

Four names, one mental model: "non-mutating version of the classic method." Browser + Node support is universal as of 2024. Use these. Half of immutability boilerplate disappears overnight.

Array.prototype.findLast and findLastIndex #

[1, 2, 3, 4].findLast(n => n < 3);       // 2
[1, 2, 3, 4].findLastIndex(n => n < 3);  // 1

Finally — no more arr.slice().reverse().find(...). Universal support.

Hashbang grammar #

#!/usr/bin/env node
console.log('Hello from a Node script');

The #! on the first line is now valid JS syntax. Node already supported it; the spec catches up. Useful only for CLI scripts.

Symbols as WeakMap keys #

Covered in Part 10. Non-registered Symbols can now key WeakMaps, enabling tag-without-wrapper patterns.

ES2024 highlights (well-supported as of 2026) #

Object.groupBy and Map.groupBy #

The one you've been hand-rolling forever:

const items = [
  { type: 'fruit', name: 'apple' },
  { type: 'veg',   name: 'carrot' },
  { type: 'fruit', name: 'banana' },
];

Object.groupBy(items, item => item.type);
// {
//   fruit: [{ type: 'fruit', name: 'apple' }, { type: 'fruit', name: 'banana' }],
//   veg:   [{ type: 'veg', name: 'carrot' }],
// }

Map.groupBy(items, item => item.type);
// Map { 'fruit' => [...], 'veg' => [...] }

The Map.groupBy version is essential when you want object keys (not just strings) for the groups:

Map.groupBy(items, item => item.category);   // category is an object reference

Replaces 4-line reduce boilerplate across thousands of codebases. Use it.

Promise.withResolvers #

A cleaner alternative to the classic deferred pattern:

// Before — assigning resolve/reject inside an executor:
let resolve, reject;
const promise = new Promise((res, rej) => {
  resolve = res;
  reject = rej;
});

// After:
const { promise, resolve, reject } = Promise.withResolvers();

Replaces a 5-line pattern with one line. Useful for queues, event-driven adapters, and anywhere you need to resolve a promise from outside its executor.

Well-formed Unicode strings #

const s = '\uD800';                 // unpaired surrogate — was invalid UTF-16
s.isWellFormed();                    // false
s.toWellFormed();                    // 'A' (replacement char) — safe to serialize
JSON.stringify(s);                    // would have thrown in some environments

Narrow but useful for code that handles untrusted strings (emoji parsing, anti-spam, log normalization).

ES2025 (just shipped or shipping) #

Iterator helper methods #

Iterators get the same map, filter, take, drop, flatMap, forEach, toArray, reduce that arrays have — but lazily:

function* naturals() {
  let n = 1; while (true) yield n++;
}

naturals()
  .filter(n => n % 2 === 0)
  .map(n => n * n)
  .take(5)
  .toArray();
// [4, 16, 36, 64, 100]

The equivalent today (without these helpers) requires building your own generator pipeline. With them, generators get array-like ergonomics with lazy semantics. Huge for stream processing and infinite sequences. Browser support is growing in 2026.

Set methods (union, intersection, difference, etc) #

const a = new Set([1, 2, 3]);
const b = new Set([2, 3, 4]);

a.union(b);              // Set { 1, 2, 3, 4 }
a.intersection(b);       // Set { 2, 3 }
a.difference(b);         // Set { 1 }
a.symmetricDifference(b);// Set { 1, 4 }
a.isSubsetOf(b);         // false
a.isSupersetOf(b);       // false
a.isDisjointFrom(b);     // false

Finally. Set arithmetic without lodash. Use it.

RegExp.escape #

const userInput = 'foo.bar?';
const re = new RegExp(`^${RegExp.escape(userInput)}$`);  // safely match literal

The pattern of escaping regex special chars used to require a hand-rolled helper. Now built in.

Stage-3 proposals worth watching #

Not yet shipped but well-supported in toolchains:

Decorators #

Class and method decorators are now Stage 3 and shipping in TypeScript 5+:

@logged
class Service {
  @memoized
  async fetch(id) { /* ... */ }
}

React, Angular (legacy decorators), MobX, NestJS — many ecosystems already use them. Native support is rolling out.

Pipeline operator |> #

Long-debated proposal that adds:

result = value |> double |> square |> negate;

Reads like pipe(...) from Part 17 but built into the language. Still Stage 2; ship date unclear.

Records and Tuples #

Deeply immutable, value-equality versions of objects and arrays:

const point1 = #{ x: 1, y: 2 };
const point2 = #{ x: 1, y: 2 };
point1 === point2;  // true — value equality!

Game-changer for React, Redux, and any state-comparing system. Stage 2 — track but don't depend on it yet.

What you can skip (for now) #

  • Atomics + SharedArrayBuffer — niche worker-thread features. Skip unless you're writing high-perf computation across workers.
  • Temporal API — replaces the awful Date API. Stage 3, browser support landing in 2026. Watch this one — when it ships, switch immediately. Until then, use date-fns or dayjs.
  • Explicit Resource Management (using) — Stage 3. Adds a using keyword that calls a cleanup method when the variable goes out of scope. Useful for streams, locks, transactions. Worth adopting once your runtime supports it.
  • Realms — for isolating JS contexts. Stage 2; rarely useful in app code.

What 2026 best-practice looks like #

Given everything across this series, the modern style guide:

  • const default, let when reassigning, never var. (Part 2)
  • === always, == null only. (Part 6)
  • Arrow functions for callbacks; function for methods that need this. (Part 5)
  • async/await over .then chains. (Part 8)
  • Non-mutating array methods: toSorted, toReversed, toSpliced, with, findLast. (this article)
  • Object.groupBy / Map.groupBy instead of hand-rolled reduce. (this article)
  • Compose with pipe, not deep call nesting. (Part 17)
  • Pure cores, impure shells. (Part 16)
  • WeakMap for per-object metadata; class #fields for hard privacy. (Part 10)
  • Error.cause when re-throwing. (Part 11)
  • Always use modules; never var globals. (Part 3)
  • Generators for lazy streams; async generators for async streams. (Part 15)

Apply these and your code looks current in 2026.

Common gotchas #

Gotcha 1: Polyfill check the new methods before assuming support.

if (typeof Object.groupBy !== 'function') { /* polyfill */ }

Most modern environments support ES2024 but a stray older Node or embedded engine will still bite.

Gotcha 2: toSorted returns a NEW array — don't forget to assign.

arr.toSorted();           // value discarded
const sorted = arr.toSorted();  // ✓

Gotcha 3: Promise.withResolvers is not a constructor.

new Promise.withResolvers();  // TypeError
Promise.withResolvers();      // ✓

Gotcha 4: Object.groupBy returns a plain object (string keys); Map.groupBy returns a Map (any keys). If your group key is an object, you must use Map.groupBy. Object.groupBy will silently coerce object keys to '[object Object]'.

Closing the series #

If you read every part of this series, here's what you've covered:

  1. Execution Context & Call Stack — the mental model behind everything else
  2. Hoisting Demystified — the creation phase and TDZ
  3. Lexical Scope — how variable lookups work
  4. Closures — functions that remember their environment
  5. The this Keyword — five binding rules
  6. Type Coercion & Equality== vs ===, falsy/truthy
  7. The Event Loop — microtasks vs macrotasks
  8. Promises from Scratch — the 60-line implementation
  9. Memory & Garbage Collection — leak patterns and DevTools
  10. WeakMap, WeakSet, WeakRef — weak references explained
  11. Error Handling Patterns — async, Result types, Error.cause
  12. Prototypes & Prototype Chainclass decoded
  13. Inheritance Patterns — classes vs composition vs mixins
  14. Symbols — the 7th primitive
  15. Iterators & Generators — lazy sequences
  16. Pure Functions & Side Effects — the functional-core/imperative-shell pattern
  17. Higher-Order Functions & Compositionpipe, compose, currying
  18. ES2024+ Worth Using — modern syntax + this best-practice cheat sheet

That's the whole stack. Every other JavaScript topic — frameworks, build tools, type systems, runtime APIs — builds on these.

Recap #

  • ES2023toSorted, toReversed, toSpliced, with, findLast. Use immediately.
  • ES2024Object.groupBy, Map.groupBy, Promise.withResolvers. Use immediately.
  • ES2025 — iterator helpers, Set methods, RegExp.escape. Use as runtime support catches up.
  • Stage 3 watch list — decorators (use via TS today), Temporal, using.
  • The 2026 JS style guide is short: const, ===, async/await, immutable arrays, pure cores, modules.

Thanks for reading the series. If something landed (or didn't), let us know — every article above gets updated as the language evolves.

Up next in JavaScript

More from this topic

View all JavaScript articles →

Enjoyed this article?

Get new JavaScript tutorials delivered. No spam — just code-first articles when they ship.

Leave a Comment

Your email stays private. Required fields are marked *

Leave a Comment

Your email stays private. Required fields are marked *