JavaScript Modern Features: ES2023 to ES2026 in Practice

JavaScript evolves faster than most people realize: ECMAScript ships a new edition every June, and many practical features land individually and reach Baseline quickly instead of waiting for a "major release". For frontend engineers, mastering these native capabilities significantly reduces reliance on third-party utility libraries and makes code more readable and closer to the language's own design.

These capabilities are part of the daily foundation of the frontend building category. They pair best with the TypeScript guide; for the engineering side, see the frontend toolchain 2026.

Change-Array-by-Copy Methods

ES2023 introduced the Change Array by Copy family (marked Baseline Widely available on MDN): toSorted(), toReversed(), toSpliced() and with(). They mirror sort/reverse/splice but do not mutate the original array; they return a new one, making immutable updates (with React, Zustand, and so on) much more ergonomic:

const months = ['Mar', 'Jan', 'Feb', 'Dec'];
const sorted = months.toSorted();        // ['Dec', 'Feb', 'Jan', 'Mar']
const reversed = months.toReversed();
const next = months.with(0, 'May');      // replace element at index 0
console.log(months);                      // original unchanged

ES2024 added Object.groupBy() and Map.groupBy(), which group an array by the key returned from a callback, replacing hand-written reduce grouping:

const byGrade = Object.groupBy(students, s => s.score >= 60 ? 'pass' : 'fail');

You can remember the mapping this way: add to to the old method name, or use with instead of direct index assignment. All of them return a new array and leave the original untouched. The only difference between Map.groupBy and Object.groupBy is the return type — a Map rather than a plain object — which suits cases where you need numeric keys or custom key types.

Old method New copy method Returns
sort() toSorted() new array
reverse() toReversed() new array
splice() toSpliced() new array
arr[i] = v with(i, v) new array

Async and Promise Enhancements

  • Promise.withResolvers() (ES2024): returns the promise, resolve and reject in one call, avoiding the "external callback" pattern needed to resolve outside the constructor;
  • Promise.try() (ES2025): wraps a function that may be sync or async into a uniform promise, with consistent error handling and no async wrapper needed;
  • Array.fromAsync() (ES2024): builds an array directly from an async iterable, such as a streaming interface.

RegExp and String Enhancements

  • The v flag (unicodeSets, ES2024): string properties and set operations (difference, intersection, union) for much more powerful character classes, e.g. [\p{Script=Han}&&\p{Letter}] matches Han script letters;
  • RegExp.escape() (ES2025): escapes user input so dynamically constructed patterns do not break;
  • String.prototype.isWellFormed() / toWellFormed() (ES2024): detect and fix lone surrogates in strings coming from external systems;
  • JSON.parse supports a source option (ES2025) to recover the original text.

Iterators and Utility Methods

The ES2025 Iterator Helpers finally give iterators map/filter/take/drop/reduce/toArray, enabling more declarative data pipelines with generators:

function* fib() { /* ... */ }
const first10 = [...fib()].filter(n => n % 2 === 0).toArray();

Note that the native Array.prototype.filter returns an array, while the iterator version returns a new iterator that can keep chaining, and combined with take(5) it also gives you lazy evaluation.

TC39 Proposals Worth Watching

As of mid-2026, these proposals are at Stage 3 (near shipping) or Stage 2.7 and worth knowing about (see the TC39 proposals repository):

  • Stage 3: iterator utilities (chunking, join, includes), RegExp buffer boundaries \A/\z/\Z, Error Stack Accessor, Await Dictionary, dynamic code brand checks;
  • Stage 2.7: Decorators (with metadata), ShadowRealm (an isolated JS execution environment), ESM Phase Imports, immutable ArrayBuffer;
  • Stage 2: the pipeline operator, Math.clamp(), Iterator.range(), JSON.parseImmutable(), Structs, Amount (arbitrary-precision decimal money) and more;
  • Temporal: the date/time proposal, currently at Stage 3, aims to replace the flawed Date design with a complete API covering time zones, calendars and Duration. Date-sensitive applications should evaluate it early.

Adopting Modern Features Safely

  1. Check the Baseline badge: MDN marks every feature as "Baseline Widely available"; do not ship to production before that;
  2. Let the compiler handle it: Babel/TypeScript transpile newer syntax for older environments, and core-js polyfills missing built-ins such as toSorted;
  3. Adopt on demand: use Object.groupBy instead of a lodash grouping function to shrink bundle size;
  4. Pair with TS types: TypeScript's lib setting exposes types for new methods (see the TypeScript guide).

Reference: https://developer.mozilla.org/en-US/docs/Glossary/Baseline/Compatibility

A real-world scenario: state updates and data pipelines

New features are best appreciated in real code. Take a React state update: where you used to copy an array before mutating it, you can now use the copy methods directly:

const [items, setItems] = useState(['Mar', 'Jan', 'Feb']);

// sort without touching the original
const sorted = items.toSorted();
// replace one index
setItems(items.with(1, 'May'));

Paired with Object.groupBy, data from a single request can be grouped before rendering, with no lodash import:

const orders = await fetch('/api/orders').then(r => r.json());
const byStatus = Object.groupBy(orders, o => o.status);
// byStatus = { paid: [...], pending: [...], refunded: [...] }

Iterator methods shine with "infinite streams": take stops the pipeline as soon as enough data is available, without materializing the whole sequence. When lazily loading pages from a paginated API, you can break after processing one page instead of waiting for everything.

FAQ

  • Can I use these features in production? Check the MDN Baseline badge. Once a feature is marked "Baseline Widely available", mainstream browsers need no polyfill; if you are still cautious, add core-js before shipping.
  • How much slower is toSorted than sort? Copy methods add one array copy, which is measurable on arrays with millions of elements; but most frontend data is nowhere near that size, and paying a little memory for immutability is usually worth it.
  • Object.groupBy or a hand-written reduce? Given the same semantics, prefer the native method — shorter code, clearer intent. Hand-write only when you need a custom grouping object prototype or complex aggregation.
  • Will old browsers break after upgrading syntax? Let Babel/TS transpile the syntax and polyfills handle the built-ins; pin the target browser list with browserslist in CI and let core-js inject only what is missing.

16IDC Takeaway

Every batch of landed language features reduces the "infrastructure debt" in frontend code. For indie developers and small teams, mastering native capabilities first and deciding whether to add a library second is usually the best balance between bundle size, maintainability and hiring bar. For multilingual sites, standard APIs such as Intl and Temporal are especially worth learning early.

Source: https://github.com/tc39/proposals