Affecter une variable à une autre semble toujours faire la même chose. Avec un nombre, la seconde variable devient indépendante. Avec un objet, les deux noms désignent la même chose, et modifier l'une modifie l'autre.
Cette asymétrie n'est pas une bizarrerie de JavaScript : la plupart des langages fonctionnent ainsi. Encore faut-il savoir de quel côté se trouve chaque type.
Définition
Le premier niveau est bien détaché : profil.nom vaut toujours "Ada". Changez maintenant la cible pour copie.adresse.ville : c'est là que la protection s'arrête.
Le tableau représente la mémoire de façon simplifiée : sur les deux dernières lignes, chaque cadre plein est un objet, et un cadre à cheval sur les deux colonnes est un objet unique atteint par les deux noms. Les cadres en pointillés du haut sont des valeurs, pas des objets, et ne comptent pas. { ...profil } ne recopie que les propriétés propres et énumérables du premier niveau, et transforme les accesseurs (getters) en valeurs figées. structuredClone() a lui aussi ses limites : il refuse une fonction et rend l'objet sans son prototype, donc sans ses méthodes de classe. Le nombre, lui, n'a pas de mode de copie : une primitive est recopiée à chaque affectation, il n'y a aucune adresse à partager.
Modifiez la copie et regardez lequel des deux originaux a bougé. Le troisième cas, celui de l'objet imbriqué, est celui qui surprend le plus.
Une valeur primitive est copiée à chaque affectation : la nouvelle variable reçoit son propre exemplaire. Un objet ne l'est pas : la variable ne contient qu'une référence, une adresse, et l'affecter ailleurs ne fait que dupliquer cette adresse.
- Copiés par valeur : Number (nombre), String (chaîne de caractères), Boolean (booléen), null, undefined, Symbol et BigInt.
- Partagés par référence : Object (objet), Array (tableau), function, Date, Map et Set.
let a = 10;
let b = a;
b += 5;
console.log(a, b); // 10 15
const objetA = { total: 10 };
const objetB = objetA;
objetB.total += 5;
console.log(objetA.total, objetB.total); // 15 15Dans le second cas, aucun objet n'a été créé à la ligne d'affectation. Il n'y a qu'un objet et deux noms qui pointent dessus.
Ce que ça change pour l'égalité
Deux objets au contenu identique ne sont jamais égaux : la comparaison porte sur l'adresse, pas sur le contenu.
console.log({ x: 1 } === { x: 1 }); // false
console.log([1, 2] === [1, 2]); // false
const partage = { x: 1 };
console.log(partage === partage); // trueC'est vrai pour l'Égalité stricte (===) comme pour l'Égalité faible (==). Pour comparer des contenus, il faut soit comparer champ par champ, soit passer par une sérialisation avec JSON, en gardant en tête que l'ordre des clés compte alors.
Copier pour de vrai
const profil = { nom: "Ada", adresse: { ville: "Londres" } };
const superficielle = { ...profil };
superficielle.adresse.ville = "Paris";
console.log(profil.adresse.ville); // Paris, l'imbriqué est partagé
const profonde = structuredClone(profil);
profonde.adresse.ville = "Lyon";
console.log(profil.adresse.ville); // Paris, l'original est protégéLe Spread (...) ne copie qu'un niveau. Pour une structure imbriquée, structuredClone() produit une copie complète, y compris des dates et des Map, là où un passage par JSON les aurait perdues.
Questions fréquentes
Un objet passé en argument est-il modifiable par la fonction ?
Oui, la fonction reçoit la même référence et peut modifier le contenu. En revanche, réaffecter le paramètre à l'intérieur ne change rien pour l'appelant : seule la copie locale de l'adresse est remplacée. C'est la nuance qui distingue modifier un objet de le remplacer.
Pourquoi deux dates identiques ne sont-elles pas égales ?
Parce qu'une Date est un objet, donc comparée par référence. Deux instances construites sur le même instant restent deux objets distincts. La comparaison fiable passe par leur valeur numérique, avec a.getTime() === b.getTime().
Les chaînes sont-elles vraiment copiées à chaque fois ?
Du point de vue du langage, oui : une chaîne se comporte comme une valeur et ne peut pas muter. En interne, les moteurs partagent la mémoire tant que personne ne modifie rien, mais cette optimisation est invisible et ne change aucun comportement observable.