Un tableau modifié à un endroit du code peut changer sous les pieds d'un autre endroit qui le tenait déjà. Le bogue est silencieux, apparaît loin de sa cause, et résiste bien à la relecture.
L'immuabilité coupe court à ce scénario en posant une discipline simple : on ne modifie rien, on produit une nouvelle valeur.
Définition
L'immuabilité est le principe selon lequel une valeur, une fois créée, n'est plus modifiée. Toute évolution passe par la création d'une copie portant le changement. L'ancienne version reste intacte pour ceux qui la détenaient.
const panier = ["clavier", "souris"];
const avecEcran = [...panier, "écran"];
console.log(panier); // [ 'clavier', 'souris' ]
console.log(avecEcran); // [ 'clavier', 'souris', 'écran' ]
const profil = { nom: "Ada", ville: "Londres" };
const demenage = { ...profil, ville: "Paris" };
console.log(profil.ville); // Londres
console.log(demenage.ville); // ParisLe Spread (...) fait tout le travail : il recopie le contenu dans une structure neuve, et ce qui est écrit après lui remplace ce qui vient d'être copié.
Le geste équivalent, en version immuable
Chaque opération qui modifie sur place a son pendant qui renvoie une copie. Ce tableau à trois colonnes couvre les quatre cas quotidiens.
| Besoin | Version qui modifie | Version qui copie |
|---|---|---|
| Ajouter en fin | liste.push(x) | [...liste, x] |
| Retirer un élément | liste.splice(i, 1) | liste.filter(...) |
| Trier | liste.sort() | liste.toSorted() |
| Changer un champ | obj.champ = v | { ...obj, champ: v } |
Object.freeze et sa limite
const empêche de réaffecter un nom, pas de modifier ce qu'il contient. Object.freeze() va plus loin, mais s'arrête au premier niveau.
const config = Object.freeze({ langue: "fr", options: { son: true } });
console.log(Object.isFrozen(config)); // true
console.log(Object.isFrozen(config.options)); // false, le gel ne descend pas
config.options.son = false;
console.log(config.options.son); // falseÉcrire config.langue = "en" est ignoré en silence dans un script classique, et lève une TypeError dans un module. Le gel protège donc, mais pas de la même façon partout.
Questions fréquentes
Copier sans arrêt ne coûte-t-il pas cher ?
Une copie superficielle ne duplique que le premier niveau : les valeurs imbriquées sont partagées, pas recréées. Sur des listes de quelques milliers d'éléments, le coût est sans commune mesure avec le temps passé à traquer une Mutation imprévue. Le vrai risque serait la copie profonde systématique, rarement nécessaire.
const rend-il une valeur immuable ?
Non, et c'est la confusion la plus répandue. const interdit de réaffecter le nom, rien de plus. Un tableau déclaré avec const accepte parfaitement push(), parce que la variable pointe toujours vers le même tableau. Voir Référence et valeur pour le détail.
Pourquoi les interfaces modernes y tiennent-elles autant ?
Parce qu'une bibliothèque d'affichage détecte un changement en comparant deux références. Si l'objet a été modifié sur place, l'ancienne et la nouvelle sont identiques et rien ne se met à jour. Notre formation React revient sur ce point dès la première mise à jour d'état, là où la règle devient concrète.