Modern JavaScript: The Orchestrator

5 min read

Introduction

For years, JavaScript was missing batteries. If you wanted to deep clone an object, you installed Lodash. If you wanted to format a date, you installed Moment.js. If you wanted to group an array, you wrote a custom reducer.

We became hoarders of dependencies. Our package.json files grew fat with libraries that solved problems the language itself ignored. We justified it as "developer velocity," but we were accumulating technical debt.

But in 2026, the era of the "Utility Library" is ending. The ECMAScript committee has spent the last few years systematically adding standard library features that make these external dependencies obsolete.

Modern JavaScript is no longer just a scripting language; it is a robust Orchestrator. Its job is not to do everything itself, but to coordinate the powerful capabilities of the browser platform.

The Death of Lodash: Native Utilities

The most significant shift in recent years is the addition of high-level data manipulation methods directly to the prototype chain.

Object.groupBy()

Grouping data is one of the most common tasks in frontend development (e.g., grouping transactions by date, or products by category). We used to reach for _.groupBy. Now, it's native.

const inventory = [
  { name: 'Asparagus', type: 'vegetables', quantity: 5 },
  { name: 'Bananas', type: 'fruit', quantity: 0 },
  { name: 'Goat', type: 'meat', quantity: 23 },
  { name: 'Cherries', type: 'fruit', quantity: 5 },
  { name: 'Fish', type: 'meat', quantity: 22 },
];

const result = Object.groupBy(inventory, ({ type }) => type);

/* Result is:
{
  vegetables: [{ name: 'Asparagus', ... }],
  fruit: [{ name: "Bananas", ... }, { name: "Cherries", ... }],
  meat: [{ name: "Goat", ... }, { name: "Fish", ... }]
}
*/

structuredClone()

Deep cloning objects used to be a notorious interview question. The answers ranged from the hacky JSON.parse(JSON.stringify(x)) (which breaks Dates and undefined) to the heavy lodash.cloneDeep.

structuredClone() is now the platform standard. It uses the same algorithm as postMessage, meaning it handles:

  • Circular references
  • Date objects
  • Map and Set
  • ArrayBuffer and Blob
const original = {
  created: new Date(),
  socials: new Set(['twitter', 'linkedin']),
};

// Zero dependencies, fully recursive clone
const copy = structuredClone(original);

Senior Gotcha: structuredClone cannot clone functions or DOM nodes. If you try to clone an object with a method attached to it, it will throw a DataCloneError. This is a feature, not a bug—data should be data, behavior should be behavior.

Native Set Theory

For a decade, finding the intersection of two arrays required an external library. New Set methods (intersection, union, difference) make this trivial.

const admins = new Set(['alice', 'bob', 'charlie']);
const online = new Set(['bob', 'dave']);

// Who is an admin AND online?
const onlineAdmins = admins.intersection(online); 
// Set { 'bob' }

// Who is EITHER an admin OR online?
const everyone = admins.union(online);
// Set { 'alice', 'bob', 'charlie', 'dave' }

Fixing Time: The Temporal API

The JavaScript Date object is famously broken. It mimics the Java 1.0 Date class from 1995. It mutates in place, uses 0-indexed months (January is 0?), and parses strings inconsistently.

We patched this with Moment.js, then date-fns, then Day.js.

The Temporal API is the native fix. It introduces immutable, timezone-aware types that distinguish between "Wall Clock Time" (PlainDateTime) and "Absolute Time" (Instant).

// Current Date (Legacy)
const d = new Date();
d.setMonth(d.getMonth() + 1); // Mutation! 'd' is now changed.

// Temporal (Modern)
const now = Temporal.Now.plainDateISO();
const nextMonth = now.add({ months: 1 }); // Returns NEW object

console.log(now.toString());       // 2026-01-20
console.log(nextMonth.toString()); // 2026-02-20

Senior Take: Temporal is currently Stage 3, but Chrome 144 ships it stable this month. You should be writing Temporal code today (using the polyfill) and removing it once browser support hits your target baseline.

Explicit Resource Management: The using Keyword

Memory leaks in JavaScript often come from forgetting to clean up resources: closing file handles, disconnecting sockets, or removing event listeners.

Inspired by C#'s using and Python's with, JavaScript now has Explicit Resource Management.

The Old Way (try/finally)

const connection = openConnection();
try {
  // do work
} finally {
  connection.close(); // Easy to forget!
}

The New Way (using)

When you declare a variable with using, its [Symbol.dispose] method is automatically called when the variable goes out of scope (i.e., the block ends).

function processFile(path) {
  using file = openFile(path); // Automatically closed when function returns!
  
  const content = file.read();
  return JSON.parse(content);
}

Async Cleanup

For resources that need to perform I/O to close (like a Database connection), we use await using.

{
  await using db = await connectToDatabase();
  await db.query('SELECT * FROM users');
} // db.disconnect() is awaited here automatically

Top-Level Await: Simplifying Modules

Historically, await could only be used inside an async function. This led to the "IIFE Wrapper" pattern whenever we needed to do asynchronous initialization in a module (like connecting to a database or loading a config).

Top-Level Await allows modules to act as big async functions.

// db.js
const connection = await connectToDB(); // Blocks import until connected!
export default connection;
// app.js
import db from './db.js'; // Waits for db to be connected
// When this line runs, db is guaranteed ready

Trade-off: This makes imports blocking. If db.js takes 5 seconds to connect, app.js will not execute for 5 seconds. Use this power wisely—only for critical dependencies that the app literally cannot run without.

Iterator Helpers: Laziness is a Virtue

JavaScript generators are powerful but often underused because they lack array methods like map and filter. You had to convert them to an array (losing the lazy evaluation benefit) or write custom loops.

Iterator Helpers bring array methods to iterators.

function* fibonacci() {
  let [prev, curr] = [0, 1];
  while (true) {
    yield curr;
    [prev, curr] = [curr, prev + curr];
  }
}

// Take the first 5 even Fibonacci numbers
const evens = fibonacci()
  .filter(n => n % 2 === 0)
  .take(5)
  .toArray(); 
  
// [2, 8, 34, 144, 610]

This allows for efficient processing of infinite streams or massive datasets without loading everything into memory.

Conclusion: The Platform Wins

The history of web development is a cycle:

  1. The platform is missing a feature.
  2. Libraries emerge to fill the gap (jQuery, Lodash, Moment).
  3. The platform adopts the best ideas (querySelector, ES6, Temporal).
  4. The libraries become legacy debt.

We are currently at Step 3 for Data, Time, and Resource Management. The most performant code is the code you don't write. Orchestrate the platform, don't wrap it.

What to do next: Scan your package.json. Do you have lodash? uuid? deep-equal? Check if crypto.randomUUID(), structuredClone(), or Set operations can replace them. Delete code today.


This is the final part of our Modern Triad series. Read Part 1: Overview, Part 2: Semantic HTML, and Part 3: CSS Architecture.