ES2023 New JavaScript Features You Can Use in WordPress Development

ECMAScript 2023 (ES14) introduced several practical additions to the JavaScript language that are fully supported in all modern browsers and in Node.js 20+ — making them usable in WordPress JavaScript that targets modern browsers via a build step, in wp-scripts projects, and in Node.js tooling scripts. The most useful additions for WordPress developers: Array.prototype.findLast() and Array.prototype.findLastIndex() search an array from the end for the first element matching a predicate — useful for finding the most recent item in a sorted array without reversing the array first (reversing creates a copy, which is wasteful for large arrays). Array.prototype.toReversed(), Array.prototype.toSorted(), and Array.prototype.toSpliced() are the immutable versions of reverse(), sort(), and splice() — they return a new array instead of mutating the original, enabling functional programming patterns without needing to manually copy the array before mutating it. These methods pair naturally with Proxy-based reactive stores (mutation of the original array would bypass Proxy traps; immutable methods produce a new array that can be assigned to the reactive state property, triggering the set trap correctly). Array.prototype.with(index, value) returns a copy of the array with the element at the specified index replaced — the immutable equivalent of array[index] = value. For Set: the ES2024 Set methods (Set.prototype.union(), Set.prototype.intersection(), Set.prototype.difference()) were not in ES2023 but are already available in Chrome 122+ and Safari 17+. Change array by copy is the collective name for toReversed, toSorted, toSpliced, and with. Hashbang support (#!/usr/bin/env node as the first line of a JS file) is now part of the spec, formalizing what Node.js already supported. The reactive state management post used Proxy for reactive stores; ES2023 immutable array methods are the natural companion for mutating state in those stores.

Problem: A WordPress admin JavaScript module manages a list of notifications — the code uses Array.reverse() to show the most recent first, but this mutates the shared state array and breaks the Proxy-based reactive store; sort() and splice() similarly mutate the original array, requiring manual [...array] spread copies before every sort and filter operation.

Solution: Replace the mutating array methods with ES2023 immutable equivalents — toReversed(), toSorted(), toSpliced(), and with() — which return new arrays and work correctly with Proxy reactive stores.

// ── ES2023 Array methods for WordPress admin state management ───────────────

// Sample notifications state
let notifications = [
    { id: 1, text: 'Plugin activated',   timestamp: 1700000000, read: true  },
    { id: 2, text: 'Update available',   timestamp: 1700001000, read: false },
    { id: 3, text: 'Backup completed',   timestamp: 1700002000, read: false },
];

// ── findLast / findLastIndex ──────────────────────────────────────────────────
// Find the most recent unread notification (last matching from the end)
const lastUnread = notifications.findLast(n => !n.read);
// { id: 3, text: 'Backup completed', ... }

const lastUnreadIndex = notifications.findLastIndex(n => !n.read);
// 2

// ── toReversed — immutable reverse (no mutation) ──────────────────────────────
// Before ES2023:
// const reversed = [...notifications].reverse();  // manual copy needed

// ES2023:
const newestFirst = notifications.toReversed();
// notifications is unchanged; newestFirst is a new array in reverse order

// ── toSorted — immutable sort ─────────────────────────────────────────────────
// Sort by timestamp descending without mutating notifications
const sortedByTime = notifications.toSorted((a, b) => b.timestamp - a.timestamp);

// ── toSpliced — immutable splice ──────────────────────────────────────────────
// Remove 1 item at index 1 without mutating
const withoutSecond = notifications.toSpliced(1, 1);
// [item0, item2]  — notifications still has all 3 items

// Insert a new notification at index 0
const newNotification = { id: 4, text: 'Cache cleared', timestamp: Date.now(), read: false };
const withNew = notifications.toSpliced(0, 0, newNotification);
// [newNotification, ...notifications]

// ── with — immutable index replacement ───────────────────────────────────────
// Mark notification at index 1 as read
const afterMarkRead = notifications.with(1, { ...notifications[1], read: true });
// notifications[1] still has read: false; afterMarkRead[1] has read: true

// ── Integration with Proxy reactive store ────────────────────────────────────
// Because these methods return new arrays instead of mutating in place,
// assigning them to a reactive state property correctly triggers the Proxy set trap:

const { state, subscribe } = createStore({ notifications });

subscribe('notifications', (newList) => {
    renderNotificationList(newList);
});

// This triggers the Proxy set trap and calls subscribe callbacks:
state.notifications = state.notifications.toSpliced(0, 0, newNotification);

// ── Practical: filter + sort pipeline returning new arrays ────────────────────
function getDisplayNotifications(allNotifications, { showRead = false } = {}) {
    return allNotifications
        .filter(n => showRead || !n.read)          // filter returns new array
        .toSorted((a, b) => b.timestamp - a.timestamp); // toSorted returns new array
}

NOTE: If you use wp-scripts (based on webpack/babel) for your WordPress JavaScript build, these ES2023 methods are available without any polyfills when your browserslist targets modern browsers — they are native language methods, not syntax features, so Babel cannot polyfill them via transpilation. If you need to support older browsers (e.g., Safari 15.x, which lacks toSorted), add core-js with useBuiltIns: 'usage' in your Babel config, which will add the polyfill automatically when it detects Array.prototype.toSorted usage. For Node.js tooling scripts (Gulp, custom build scripts), Node.js 20+ supports all ES2023 Array methods natively — no polyfill needed.