Skip to content
tutorials

Your JavaScript Is Obsolete.

The language is evolving faster than you think, leaving common patterns and workarounds in the dust. You're probably still using hacks for problems JavaScript solved years—or even months—ago.

Dani Roth
Your JavaScript Is Obsolete.

The New Rules of Array Immutability

Modern JavaScript demands immutability, especially in frameworks like React that rely on stable state references. Mutating an array directly with methods such as sort() or reverse() breaks this contract, leading to unpredictable UI updates and difficult-to-debug side effects. These traditional methods modify the original array in place, a critical anti-pattern for robust, maintainable state management.

Solve this problem with new immutable counterparts. ECMAScript 2023 (ES14) introduced toSorted() and toReversed(), which return a new array with the changes, leaving the source array untouched. This eliminates unintended side effects, simplifying state updates and ensuring predictable component re-renders across your application.

Further enhance array immutability with with(index, value). This method cleanly replaces an element at a specific index, returning a new array without mutating the original. It offers a clear, declarative alternative to cumbersome splice() calls or direct arr[i] = val assignments, promoting a more functional and less error-prone coding style.

Adopt these indispensable methods now. They shipped as part of ECMAScript 2023 (ES14) and are fully supported in modern runtimes, including Chrome 110+, Firefox 115+, Safari 16+, and Node.js 20+. Upgrade your array operations for modern JavaScript development.

JSON.stringify is Dead for Cloning

Abandon the JSON.parse(JSON.stringify(obj)) deep cloning hack immediately. This method notoriously fails on complex data types, converting Date objects into mere strings and completely discarding Map and Set data, leaving them as empty objects. Furthermore, it cannot handle functions, undefined, Infinity, NaN, or objects with circular references, causing silent data loss or runtime errors.

Instead, adopt structuredClone(), the modern, built-in API. This definitive solution faithfully copies a wide array of complex data types, including Map, Set, Date, RegExp, ArrayBuffer, Blob, and even ImageData. Critically, structuredClone() also navigates and duplicates objects containing circular graphs, providing robust deep copies without any external libraries or custom serialization logic.

While powerful, structuredClone() does have minor, specific limitations: it cannot clone functions, DOM nodes, or properties with custom getters/setters. However, for the vast majority of application data, it provides a complete and type-preserving clone. Make structuredClone() your go-to standard for 99% of deep cloning use cases in modern JavaScript, ensuring data integrity and predictable state.

The Promise Pattern You've Been Waiting For

Resolve promises from an external scope without the boilerplate. Historically, managing a Promise's resolution outside its constructor meant declaring resolve and reject variables beforehand. This pattern, requiring external variable assignments, clutters code and makes promise control opaque.

Observe the common legacy pattern:

let externalResolve, externalReject;
const externalPromise = new Promise((resolve, reject) => { externalResolve = resolve; externalReject = reject;
});
// Later in your code...
externalResolve('Data received!');

This approach forces you to expose internal promise functions globally or across a wider scope, increasing potential for misuse and making the control flow harder to trace.

Enter Promise.withResolvers(), a new static method that simplifies this immensely. Introduced in ECMAScript 2024, it returns an object containing the promise, its resolve function, and its reject function. No more pre-declaration or scope leakage.

Implement Promise.withResolvers():

const { promise, resolve, reject } = Promise.withResolvers();
// ... now 'promise' can be returned, and 'resolve'/'reject' called elsewhere.
resolve('Operation completed.');

This method dramatically cleans up asynchronous patterns where a promise must await an external trigger, like a user clicking a button or an incoming WebSocket message. It's available in Chrome 120+, Firefox 121+, and Node.js 21+. For more foundational JavaScript details and evolving syntax, consult resources like Lexical grammar - JavaScript - MDN Web Docs. Embrace modern JavaScript for cleaner, more robust async control.

Enjoying this? Get one like it in your inbox each morning.

one email a day · unsubscribe in two clicks · no third-party tracking

Syntax Secrets Hiding in Plain Sight

Upgrade number readability using numeric separators, an ES2021 feature. Instantly clarify large literals like 1_000_000_000 compared to 1000000000. This syntax works for any number base—decimal, binary, octal, hexadecimal, and BigInts—adding zero performance overhead. Underscores cannot appear at the start, end, or next to a decimal point, nor can two be in a row. Crucially, this is a code-only visual separation; string parsing fails, as Number('1_000') returns NaN and parseInt('1_000') yields 1.

Take explicit control over nested loops with labeled statements. Declare myLoop: for (...) to assign a label to an outer loop. From within any inner loop, break myLoop; exits the entire labeled structure in a single, explicit step. This clean alternative eliminates boilerplate flag variables for complex control flow scenarios, significantly improving code clarity and reducing potential errors from missed breaks.

These aren't obscure language quirks for trivia contests. Leverage numeric separators and labeled statements as practical tools for writing self-documenting, maintainable code. Focus on developer ergonomics and clarity, making your JavaScript codebase immediately more comprehensible, robust, and easier for future contributors to understand.

Frequently Asked Questions

Why are immutable array methods like toSorted() important?

They prevent accidental side effects by returning a new array instead of modifying the original. This is crucial for predictable state management in modern frameworks like React, which rely on immutability to detect changes.

When should I use structuredClone() over the JSON.stringify hack?

Almost always. structuredClone() is a robust, native API that correctly handles complex data types like Dates, Maps, Sets, and even circular references. The JSON hack breaks these types and should be considered a legacy anti-pattern.

Is Promise.withResolvers() available everywhere?

No, it's a very new ECMAScript 2024 feature. It is supported in the latest versions of major browsers (like Chrome 119+) and Node.js (v22+). Always check compatibility tables if you need to support older environments.

Can numeric separators be used when parsing strings?

No. Numeric separators are a feature for number literals in your source code only. Attempting to parse a string containing an underscore with Number('1_000') or parseInt('1_000') will result in NaN or an incorrect value.

Found this useful? Share it.

For builders

Want Stork to write one of these about your product?

Send us a URL. We use the product, form a view, and publish what we actually think — in 8 languages, labeled Sponsored, with no copy approval on your side. That last part is what makes it worth quoting.

See how it works$500 · AI tools & software only