Développement Web

JavaScript moderne : ES2024 et fonctionnalités avancées

11 دقائق للقراءة

Top-level await

// Modules ESM uniquement
const config = await fetch("/config.json").then(r => r.json());
export const db = await connectDB(config.db);

Optional chaining et nullish coalescing

const user = await getUser();
const ville = user?.adresse?.ville ?? "Inconnue";
const theme = localStorage.getItem("theme") ?? "dark";
// ?. s'arrête au premier null/undefined sans lever d'erreur

Destructuring avancé

const { nom, prenom, adresse: { ville = "Dakar" } = {} } = user;
const [tete, ...reste] = ["Pierre","Marie","Jean","Amina"];
const { id, ...rest } = payload;   // enlève id, garde le reste

Array.prototype.findLast / findLastIndex

const ventes = [...]; // triées par date asc
const derniere = ventes.findLast(v => v.statut === "payee");
const idx = ventes.findLastIndex(v => v.montant > 500_000);

Array.prototype.at (index négatif)

const items = [10, 20, 30, 40];
items.at(-1);  // 40
items.at(-2);  // 30
"Bonjour".at(-1); // "r"

Object.hasOwn (remplace hasOwnProperty)

if (Object.hasOwn(obj, "email")) { /* ... */ }
// Plus sûr que obj.hasOwnProperty("email") qui peut être écrasé

Structured clone (copie profonde native)

const copie = structuredClone(original);  // profonde, gère Date, Map, Set
// Remplace JSON.parse(JSON.stringify(x)) qui perd les types

Array.prototype.group (ES2024 stage 4)

const ventes = [...];
const parRegion = Object.groupBy(ventes, v => v.region);
// { "Dakar": [...], "Thies": [...] }

const parMois = Map.groupBy(ventes, v => v.date.slice(0,7));
// Map { "2026-01" => [...], ... }

Promise.any et Promise.allSettled

// Premier OK gagne (ignore les rejects)
const resultat = await Promise.any([
  fetch("https://api1.com/data"),
  fetch("https://api2.com/data"),
  fetch("https://api3.com/data"),
]);

// Attend tout, retourne {status, value|reason}
const results = await Promise.allSettled(tasks);
const ok = results.filter(r => r.status === "fulfilled").map(r => r.value);

AbortController pour annuler fetch

const ctrl = new AbortController();
setTimeout(() => ctrl.abort(), 5000);

try {
  const res = await fetch("/lourd", { signal: ctrl.signal });
} catch (e) {
  if (e.name === "AbortError") console.log("Timeout dépassé");
}

Generators pour flux paginé

async function* pages(url) {
  let next = url;
  while (next) {
    const r = await fetch(next).then(r => r.json());
    yield* r.data;
    next = r.next_url;
  }
}

for await (const item of pages("/api/clients?page=1")) {
  console.log(item.nom);
}

Records et Tuples (Stage 2, expérimental)

// Structures immuables comparables par valeur
const point = #{ x: 10, y: 20 };
const paire = #[1, 2];
#{a:1,b:2} === #{b:2,a:1};   // true

Étape 1 : vérifier que votre runtime supporte réellement ES2024

Avant d’écrire du code ES2024, confirmez la version Node ou la cible navigateur. Node 22 LTS (sortie octobre 2024) couvre la quasi-totalité des fonctionnalités ES2024, tandis que Node 18 en supporte moins de la moitié. Côté navigateurs, Chrome 117+, Firefox 119+ et Safari 17.4+ couvrent l’essentiel. Pour une équipe à Dakar ou Abidjan déployant sur Vercel ou Netlify, le runtime cible est généralement Node 22.

node --version
# v22.11.0

node -e "console.log(typeof Object.groupBy)"
# function

Si la sortie affiche « undefined » pour Object.groupBy, votre Node est trop ancien. Mettez à jour via nvm avec nvm install 22. Validation pratique : la fonction est bien typée « function ».

Étape 2 : remplacer reduce() par Object.groupBy pour grouper des données

Le pattern reduce-pour-grouper était la verrue la plus citée en revue de code JavaScript. ES2024 introduit Object.groupBy et Map.groupBy qui rendent l’intention explicite. C’est particulièrement utile pour catégoriser des transactions ou des leads dans une application de gestion.

const transactions = [
  { id: 1, ville: 'Dakar', montant: 45000 },
  { id: 2, ville: 'Abidjan', montant: 32000 },
  { id: 3, ville: 'Dakar', montant: 18000 },
  { id: 4, ville: 'Cotonou', montant: 27000 }
];

const parVille = Object.groupBy(transactions, t => t.ville);
console.log(parVille);
// { Dakar: [...], Abidjan: [...], Cotonou: [...] }

L’output est un objet avec une clé par ville et un tableau de transactions en valeur. Map.groupBy fonctionne identiquement mais retourne une Map, utile quand vos clés ne sont pas des chaînes (objets, dates, etc.).

Étape 3 : maîtriser Promise.withResolvers pour les flux asynchrones complexes

Avant ES2024, exposer le resolve/reject d’une Promise hors de son exécuteur impliquait un boilerplate fastidieux. Promise.withResolvers retourne directement les trois pièces : promise, resolve, reject.

function attendreEvenement(emitter, evenement) {
  const { promise, resolve, reject } = Promise.withResolvers();
  emitter.once(evenement, resolve);
  emitter.once('error', reject);
  return promise;
}

await attendreEvenement(socket, 'connect');
console.log('Connecté');

Ce pattern simplifie les wrappers d’API basées sur des callbacks ou des EventEmitter. Le code reste linéaire, sans IIFE ni variables externes capturées par effet de bord. Pour un dev à Cotonou qui maintient un client WebSocket vers une API parisienne, c’est un gain de lisibilité massif sur le legacy.

Étape 4 : exploiter le flag v de RegExp pour la manipulation Unicode avancée

Le flag v (Unicode sets) remplace le flag u et autorise la notation d’ensembles, l’intersection et la différence dans les regex. Pour un site multilingue francais/wolof/bambara, c’est précieux pour matcher des classes de caractères composites.

// Lettres ASCII MAIS pas les voyelles
const regex = /[\p{ASCII}&&\p{Letter}--[aeiouAEIOU]]/v;
console.log('bonjour'.match(regex));
// ['b', 'n', 'j', 'r']

L’opérateur && fait l’intersection, -- la différence. Sans le flag v, ces opérateurs ne sont pas reconnus. Attention : v et u sont mutuellement exclusifs sur la même regex.

Étape 5 : redimensionner un ArrayBuffer sans recopier en mémoire

ES2024 permet de créer des ArrayBuffer redimensionnables, ce qui évite les recopies coûteuses lors de la lecture progressive de fichiers volumineux (logs serveur, exports CSV de plusieurs Mo). Pour une fintech à Almadies qui parse des relevés bancaires de 50 Mo, c’est une optimisation directe sur la consommation RAM.

const buffer = new ArrayBuffer(1024, { maxByteLength: 8192 });
console.log(buffer.byteLength); // 1024
console.log(buffer.resizable); // true

buffer.resize(2048);
console.log(buffer.byteLength); // 2048

Le paramètre maxByteLength fixe la borne haute. Toute tentative de resize au-delà lance une RangeError. Cette API est encore peu utilisée car la plupart des cas passent par TypedArray, mais elle brille pour les implémentations bas niveau (parsers binaires, WebAssembly).

Étape 6 : valider les chaînes Unicode bien formées avant transmission

Les surrogates orphelines (caractères UTF-16 mal appariés) plantent les API qui sérialisent en UTF-8 strict, comme TextEncoder ou la transmission HTTP. ES2024 ajoute String.prototype.isWellFormed() et toWellFormed().

const dangereux = 'Dakar\uD800';
console.log(dangereux.isWellFormed()); // false
console.log(dangereux.toWellFormed()); // 'Dakar\uFFFD'

toWellFormed remplace les surrogates orphelines par le caractère de remplacement U+FFFD. À utiliser avant tout encodeURIComponent ou JSON.stringify exposant des données utilisateur. Pour creuser l’écosystème runtime, voir notre guide Git et GitHub qui couvre le versionnement de projets Node 22 LTS.

Étape 7 : migrer progressivement sans casser la rétrocompatibilité

Pour un projet existant, n’imposez pas ES2024 d’un coup. Mettez à jour Node, activez ESLint avec la config eslint:recommended et la cible ecmaVersion: 2024. Migrez fonction par fonction lors des touches naturelles au code, et ajoutez un babel-preset-env si vous ciblez des navigateurs anciens.

Tenez un journal de migration dans le README du projet : quels fichiers ont été migrés, quelles équivalences ont été utilisées, quels tests passent en CI. Cette discipline évite les régressions silencieuses dans les équipes distribuées entre Conakry, Bamako et Paris. Pour la stack tooling complète, consultez notre tutoriel d’automatisation business qui s’appuie sur Node 22 LTS pour orchestrer les workflows.

Étape 8 : adopter top-level await dans les modules ES

Avant ES2024, await ne pouvait s’utiliser qu’à l’intérieur d’une fonction async. Cela imposait des IIFE de bootstrap dans tous les fichiers d’entrée applicatifs. Les modules ES modernes acceptent désormais top-level await, ce qui simplifie les scripts d’amorçage qui chargent une configuration distante avant tout reste du programme.

Pour un service Node 22 hébergé sur un VPS Hetzner à Cotonou qui doit charger une configuration depuis une API interne, le code de bootstrap se réduit à trois lignes lisibles.

// config.mjs
const response = await fetch('https://config.interne.local/runtime');
export const config = await response.json();

// app.mjs
import { config } from './config.mjs';
console.log('Démarrage avec', config.region);

Le moteur attend que config.mjs résolve son await avant d’exécuter app.mjs. Pas d’IIFE, pas de promesse exposée explicitement. Le risque à connaître : top-level await peut prolonger le temps de démarrage de toute votre arborescence d’imports si une dépendance lente verrouille la chaîne. Mesurez avec node --experimental-loader=./hooks.js app.mjs sur un environnement de staging avant de déployer en production.

Étape 9 : observer Node 22 en production avec les hooks intégrés

Node 22 LTS expose des APIs natives d’observabilité qui rendent moins indispensables les agents APM payants pour les TPE. Le module node:diagnostics_channel permet d’écouter les événements internes (HTTP, undici, fs) sans monkey patcher les libs.

import diagnostics from 'node:diagnostics_channel';

diagnostics.subscribe('http.client.request.start', ({ request }) => {
  console.log('[trace]', request.method, request.path);
});

Pour un développeur basé à Sicap Liberté qui veut tracer les appels sortants d’une API à 50 requêtes par seconde, ce hook capture tout sans surcoût mesurable. Combinez-le avec --inspect et Chrome DevTools pour profiler à chaud, sans attacher un agent payant.

Pour la stack DevOps autour, voyez notre tutoriel Git et GitHub qui couvre l’intégration continue de ces fichiers de configuration distants.

Étape 10 : structurer les imports dynamiques pour des bundles plus légers

L’import statique force tout le code à être livré au démarrage. ES2024 consolide la pratique de l’import dynamique avec syntaxe assertion qui charge à la demande des modules JSON, des modules CSS et des modules WebAssembly. Pour une application web servie depuis une CDN à Lagos, charger 800 ko de code seulement quand l’utilisateur clique sur la fonctionnalité d’export PDF économise une seconde de temps de chargement initial sur des connexions 3G ouest-africaines.

La syntaxe import() retourne une Promise qui résout le module entier. On l’enveloppe typiquement dans un handler d’événement utilisateur. Pour une route React rarement empruntée comme la page de paramètres administrateur, ce pattern divise par trois la taille du bundle initial sans casser la navigation.

Au-delà du gain de performance, cette pratique impose une discipline d’architecture : chaque feature lourd est découpé en modules autonomes, ce qui facilite la maintenance par plusieurs développeurs distribués entre Dakar, Abidjan et Paris. Les revues de code détectent immédiatement quand un import statique fragilise les performances de la page d’accueil.

Mesurez l’impact réel via Chrome DevTools, onglet Coverage : il affiche le pourcentage de code chargé qui n’est jamais exécuté. Au-dessus de 40 pour cent, vous avez un candidat évident pour la conversion en import dynamique. Pour la stack tooling complète qui orchestre ces optimisations, consultez notre tutoriel sur Git et GitHub qui couvre les workflows de CI/CD pour les bundles JavaScript modernes.

Étape 11 : préparer la transition vers ES2025 sans rupture

Le comité TC39 ne s’arrête pas à ES2024. Les propositions stage 3 et stage 4 de 2025 incluent les decorateurs stables, les pipe operators, les pattern matching expérimentaux et l’amélioration des Records and Tuples. Pour ne pas réécrire votre code à chaque révision, structurez-le aujourd’hui avec une discipline de modules courts, fonctions pures et états explicites.

La règle simple : chaque fichier source ne dépasse pas 200 lignes et expose au maximum trois fonctions publiques. Les structures de données complexes vivent dans des fichiers types.ts dédiés. Cette hygiène fait que la migration future vers les Records and Tuples ne touchera que ces fichiers types, pas l’ensemble du code métier.

Suivez les notes de release de Node LTS et les annonces V8 mensuelles pour anticiper les fonctionnalités qui débarquent. À Yopougon ou Niamey, l’écosystème change tellement vite qu’investir 30 minutes par mois en veille technique évite d’être surpris par des dépréciations en plein sprint. La stabilité, en JavaScript moderne, vient de l’usage rigoureux des patterns établis, pas de la course aux dernières fonctionnalités.

Étape 12 : tester les régressions de syntaxe avant migration

Avant de déployer une base de code ES2024 sur des navigateurs ou des runtimes hétérogènes en production, exécutez systématiquement la matrice de tests sur Node 18, 20, 22 et sur Chrome stable et Firefox stable. Une fonction qui passe les tests sur Node 22 peut renvoyer une erreur silencieuse sur Safari 16 si elle utilise un flag regex non encore supporté. La discipline qui sauve : un fichier package.json qui spécifie engines.node avec une version minimale, et une CI qui refuse le merge si le test ne tourne pas sur cette borne.

Pour une équipe distribuée entre Saint-Louis et Niamey, cette rigueur évite que le code marche en local sur le laptop d’un développeur Node 22 LTS et casse en production sur un container Docker basé sur Node 20 oublié dans un Dockerfile copié-collé.

Pour rester à jour sur les évolutions du langage, lisez chaque trimestre les release notes de Node.js LTS et les notes V8 publiées par Google. Les fonctionnalités stage 4 deviennent disponibles 6 à 12 mois après ratification, ce qui vous laisse le temps d’anticiper les migrations sans précipitation. Cette veille structurée évite que votre code prenne 3 ans de retard sans crier gare.

مشاركة