12k
All articles

Les nouveautés d'ECMAScript 2026

ES2026 ajoute sept nouveautés JavaScript: Array.fromAsync, Math.sumPrecise, les méthodes base64 et hex de Uint8Array, l accès source de JSON.parse et getOrInsert.

OpenReplay Team
OpenReplay Team
Les nouveautés d'ECMAScript 2026

Ecma International a approuvé la spécification du langage ECMAScript 2026 le 30 juin 2026, et celle-ci contient exactement sept nouvelles fonctionnalités : Array.fromAsync, Error.isError, Math.sumPrecise, les méthodes base64 et hexadécimales sur Uint8Array, Iterator.concat, l’accès au texte source dans JSON.parse, ainsi que getOrInsert sur Map et WeakMap.

Chaque année, ces méthodes arrivent discrètement, et la plupart d’entre nous les découvrent le jour où l’on supprime un utilitaire que l’on traînait depuis des années. Cette fois, il s’agit probablement d’un utilitaire base64.

Si vous avez fait des recherches sur ES2026 récemment, vous avez sans doute trouvé des listes qui se contredisent, dont plusieurs incluent Temporal ou le mot-clé using. Ces listes sont erronées, et cet article explique pourquoi, avant de passer en revue chacune des sept fonctionnalités réellement livrées : le problème qu’elle résout, le code qu’elle remplace, et la ligne unique qui s’y substitue.

La référence officielle est l’annonce d’approbation d’Ecma, qui confirme ES2026 comme la 17e édition d’ECMA-262, ainsi que le tableau des propositions finalisées de TC39, où exactement sept lignes portent une année de publication prévue en 2026.

Points clés

  • ES2026 a été approuvé le 30 juin 2026 et ajoute exactement sept fonctionnalités ; Temporal et Explicit Resource Management n’en font pas partie.
  • Math.sumPrecise([1e17, 1, -1e17]) renvoie 1, alors que la somme des mêmes valeurs avec reduce renvoie 0.
  • Uint8Array gagne les méthodes natives toBase64(), toHex(), fromBase64() et fromHex(), éliminant une dépendance courante.
  • Le reviver de JSON.parse reçoit désormais un troisième argument exposant le texte source brut, et JSON.rawJSON() corrige le côté stringify, ce qui permet aux BigInt de faire un aller-retour complet à travers JSON.
  • Map.prototype.getOrInsert(key, value) renvoie la valeur existante lorsque la clé est déjà présente ; il n’insère votre valeur par défaut que si la clé est absente.

Qu’est-ce qui n’a pas été retenu dans ES2026 ?

Temporal et Explicit Resource Management (using / await using) ne font pas partie d’ES2026. Dans la liste des propositions finalisées de TC39, les deux portent une année de publication prévue en 2027, aux côtés d’Atomics.pause et de Joint Iteration. Ces quatre propositions ont atteint le stage 4, l’étape finale du processus TC39, après la date limite d’ES2026. Les articles publiés avant l’approbation, fin 2025, prédisaient massivement leur arrivée en 2026, ce qui explique l’origine des listes de fonctionnalités contradictoires. La vérification que tout le monde peut effectuer : filtrer la colonne « Expected Publication Year » du tableau des propositions finalisées. Sept propositions affichent 2026, et aucune d’elles n’est Temporal.

Array.fromAsync

Array.fromAsync prend un itérateur asynchrone et vous renvoie un tableau, encapsulé dans une promesse. Elle accepte également un itérateur synchrone dont les valeurs sont des promesses, un objet de type tableau (array-like) et une fonction de mapping facultative, exactement comme Array.from. Jusqu’à présent, collecter un itérateur asynchrone impliquait une boucle for await alimentant un tableau mutable :

const arr = [];
for await (const n of asyncGen(1, 3)) {
  arr.push(`page ${n}`);
}

Le même résultat s’obtient désormais avec une seule expression :

const arr = await Array.fromAsync(asyncGen(1, 3), (n) => `page ${n}`);
// [ 'page 1', 'page 2', 'page 3' ]

Error.isError

Error.isError(value) renvoie true uniquement lorsque la valeur capturée est un véritable objet Error. JavaScript permet de lancer (throw) n’importe quoi, et instanceof Error devient trompeur dès que les valeurs franchissent les frontières d’iframes ou de realms, car chaque realm possède son propre constructeur Error. Un bloc catch conditionné par instanceof Error peut silencieusement passer à côté de la branche qui affiche un message utile, et les rejeux de session (session replays) de cette classe de bugs présentent une forme bien connue : l’utilisateur bloqué sur un écran qui n’indique rien alors qu’une véritable exception s’est produite en arrière-plan. Error.isError comble cette lacune.

try {
  throw new Error("session upload failed");
} catch (error) {
  Error.isError(error); // true
}

try {
  throw "session upload failed";
} catch (error) {
  Error.isError(error); // false
}

Math.sumPrecise

Math.sumPrecise additionne un itérable de nombres sans accumuler d’erreur de virgule flottante dans les résultats intermédiaires. Une chaîne reduce additionne deux à deux, si bien que les grandes et les petites magnitudes s’annulent mal :

const values = [1e17, 1, -1e17];

values.reduce((a, b) => a + b, 0); // 0
Math.sumPrecise(values);           // 1

La version avec reduce perd complètement le 1. Au-delà de la précision, cela permet aussi de se débarrasser du reducer de somme, ce classique du code répétitif.

Uint8Array : toBase64, toHex, fromBase64 et fromHex

ES2026 dote Uint8Array des méthodes natives toBase64() et toHex(), ainsi que des méthodes statiques Uint8Array.fromBase64() et Uint8Array.fromHex() pour le décodage. La conversion binaire vers texte impliquait auparavant d’intégrer une bibliothèque ou d’implémenter l’encodage à la main ; c’est une dépendance que la plupart des bases de code peuvent désormais supprimer.

const value = new Uint8Array([79, 112, 101, 110, 82, 101, 112, 108, 97, 121]);

const valueBase64 = value.toBase64(); // 'T3BlblJlcGxheQ=='
const decoded = Uint8Array.fromBase64(valueBase64);

new TextDecoder().decode(decoded); // 'OpenReplay'

Iterator.concat

Iterator.concat() enchaîne des itérateurs en séquence. Jusqu’à présent, la seule façon d’y parvenir consistait à écrire à la main un générateur parcourant chaque source et déléguant avec yield* :

function* combine(...iterators) {
  for (const source of iterators) {
    yield* source;
  }
}

Il suffit maintenant d’un seul appel, et de simples tableaux de valeurs peuvent s’insérer entre les itérateurs. Le [300] au milieu de l’exemple ci-dessous est un tableau ordinaire, qui n’a pas besoin d’être encapsulé dans Iterator.from.

const iOne = Iterator.from([100, 200]);
const iTwo = Iterator.from([400, 500]);

const combined = Iterator.concat(iOne, [300], iTwo);
Array.from(combined); // [ 100, 200, 300, 400, 500 ]

Accès au texte source dans JSON.parse

Avant ES2026, les allers-retours JSON en JavaScript étaient lossy dans les deux sens. L’analyse d’un littéral entier de grande taille l’arrondit silencieusement, et la sérialisation d’un BigInt lève une exception :

JSON.parse("9007199254740993");   // 9007199254740992
JSON.stringify(9007199254740993n); // TypeError: Do not know how to serialize a BigInt

La proposition d’accès au texte source dans JSON.parse corrige les deux côtés. JSON.parse transmet désormais à son reviver un troisième argument contenant le texte source intact, que vous pouvez convertir vous-même sans perdre de chiffres. Du côté de la sérialisation, JSON.rawJSON() permet à un replacer d’émettre une valeur telle quelle :

JSON.parse("9007199254740993", (key, value, { source }) => BigInt(source));
// 9007199254740993n

JSON.stringify(9007199254740993n, (key, value) => JSON.rawJSON(value));
// 9007199254740993

Un BigInt peut désormais survivre à un aller-retour JSON sans perdre de chiffres ni lever d’exception.

Map.prototype.getOrInsert et WeakMap.prototype.getOrInsert

La proposition Upsert apporte getOrInsert(key, value) à la fois à Map et à WeakMap, ce qui vous évite d’appeler has() puis set(). La sémantique a son importance : getOrInsert renvoie la valeur existante lorsque la clé est déjà présente. Il n’insère, et ne renvoie votre valeur par défaut, que lorsque la clé est absente.

const limits = new Map();
limits.set("uploads", 25);

limits.getOrInsert("exports", 10);   // 10  (inséré)
limits.getOrInsert("uploads", 100);  // 25  (la valeur existante l'emporte)

Notez que 100 est ignoré parce que "uploads" était déjà défini. Considérez le second argument comme une valeur par défaut, non comme un écrasement.

Que pouvez-vous supprimer dès maintenant ?

ES2026 est une version de nettoyage : chacune des sept fonctionnalités rend obsolète un motif que vous avez probablement en production aujourd’hui. La dépendance à un utilitaire base64, le reducer de somme, les blocs de garde has() puis set(), le générateur de concaténation avec yield* et la boucle de collecte for await disposent tous de remplacements natifs directs. La disponibilité varie selon les moteurs d’exécution, aussi consultez la référence MDN des objets globaux standards pour connaître l’état actuel de la prise en charge de chaque méthode avant d’abandonner vos polyfills. Commencez ensuite par le gain le plus facile dans votre base de code, qui pour la plupart des équipes consiste à remplacer les vérifications d’existence sur les Map par getOrInsert.

FAQ

Quelle est la différence entre Array.fromAsync et Promise.all ?

Array.fromAsync attend les valeurs de manière séquentielle, une par une, et accepte les itérables asynchrones, les itérables synchrones produisant des promesses, ainsi que les objets de type tableau. Promise.all exécute un itérable de promesses de façon concurrente et rejette dès qu'une promesse est rejetée. Utilisez Promise.all pour du travail parallèle et Array.fromAsync lorsque l'ordre importe ou que la source est un itérateur asynchrone. Les deux renvoient une promesse qui se résout en un tableau.

Error.isError renvoie-t-il true pour TypeError et les autres sous-classes d'Error ?

Oui. Error.isError renvoie true pour toute véritable sous-classe d'Error, y compris TypeError, RangeError, SyntaxError et les classes personnalisées qui étendent Error, car les moteurs vérifient un marqueur interne (brand) que portent les vraies erreurs plutôt que la chaîne de prototypes. Dans les navigateurs, une DOMException compte également comme une véritable erreur. La méthode renvoie false pour les objets qui ne font qu'imiter des erreurs, comme les objets simples dotés d'une propriété message ou les objets créés avec Object.create(Error.prototype).

Uint8Array.prototype.toBase64 peut-il produire du base64 compatible URL ?

Oui. toBase64 accepte un objet d'options avec une propriété alphabet valant 'base64' (la valeur par défaut, utilisant + et /) ou 'base64url' (utilisant - et _), ainsi qu'un booléen omitPadding qui supprime les caractères = de fin. Uint8Array.fromBase64 accepte la même option alphabet et une option lastChunkHandling ('loose', 'strict' ou 'stop-before-partial') qui contrôle la façon dont un dernier bloc de moins de quatre caractères est décodé.

Comment éviter de calculer une valeur par défaut coûteuse avec getOrInsert ?

Utilisez getOrInsertComputed(key, callback), que la même proposition Upsert ajoute à Map et à WeakMap. Cette méthode n'appelle le callback pour produire la valeur que lorsque la clé est absente, puis insère et renvoie le résultat ; lorsque la clé existe déjà, le callback n'est jamais exécuté et la valeur existante est renvoyée. Le simple getOrInsert évalue toujours son second argument avant l'appel, même lorsque la clé est déjà présente.

Open-source session replay

Complete picture for complete understanding

Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.

Star on GitHub12k

We use cookies to improve your experience. By using our site, you accept cookies.