Question 1Écris const backup = order;, puis exécute backup.total = 0;. Que se passe-t-il pour order.total ?
Références et copie — les pièges des tableaux et objets
Assigner un tableau passe une référence : modifier la copie change l'original. Copie superficielle et profonde.
Essaie de sauvegarder une commande avant de la modifier en écrivant const backup = order;, et corriger order plus tard change backup du même coup. Ça n'arrive jamais avec des nombres ou des chaînes.
Cet article couvre ce qui se passe réellement quand tu assignes un tableau ou un objet, et comment construire une copie sûre.
Ce que l'affectation passe — valeurs primitives vs références
Ce qui rend ce décalage difficile à traquer, c'est que ce qui se fait passer à l'affectation diffère selon le genre de valeur. Voici un point sur ce qui se recopie tel quel à l'affectation, et ce qui ne se recopie pas.
Les 5 genres — nombres, chaînes, booléens, null, et undefined (appelés primitives — des valeurs qui ne sont ni des objets ni des tableaux) — voient la valeur elle-même copiée quand tu les assignes.
Les 2 variables ne sont plus liées après cette copie, donc changer l'une ne change jamais l'autre.
Les tableaux et objets sont des types référence (des types qui gardent une information pointant vers où vit une valeur, plutôt que la valeur elle-même). L'affectation ne fait passer que cette information de localisation, donc 2 variables finissent par pointer sur exactement le même tableau ou objet.
Réécris-le à travers l'un ou l'autre nom, et tu réécris la même chose unique dans les deux cas.
// Une primitive voit sa valeur copiée
let price = 3980;
let salePrice = price;
salePrice = 2980;
console.log(price); // 3980 (non affecté)
console.log(salePrice); // 2980
// Un tableau pointe sur la même chose unique
const cart = ["Cahier A4"];
const savedCart = cart;
savedCart.push("Marqueur permanent");
console.log(cart.length); // 2 (cart a grandi aussi)
console.log(cart === savedCart); // true (ils pointent sur la même chose)
Même avec const, le contenu peut être réécrit
Ce que const fixe, c'est seulement où pointe la variable. Écris const cart = [];, puis cart.push("Marqueur permanent"), et le contenu grandit ; const order = {}, puis order.total = 0, fonctionne aussi.
Ce qui est interdit, c'est seulement la réaffectation qui la fait pointer ailleurs entièrement, comme cart = [...].
Construire une copie — ce que === regarde vraiment
Comme tu viens de le voir, l'affectation seule ne donne pas une copie sûre, séparée de l'original. Tu veux une commande séparée avec juste le montant changé, en laissant l'original intact ? Construis un nouvel objet disposant le contenu avec la syntaxe spread, comme dans { ...order }. Ce qui vaut la peine d'être confirmé, c'est la relation entre ce que tu construis et l'original.
const copiedOrder = { ...order }; construit un objet séparé, donc order === copiedOrder vaut false. Pour les tableaux et objets, === vérifie non pas si le contenu correspond, mais s'ils sont exactement la même chose unique.
Pour comparer le contenu, écris toi-même les choses spécifiques que tu veux comparer — le nombre de clés, des valeurs individuelles, etc.
true quand ils pointent sur la même chose, et false pour des choses construites séparément, même avec un contenu identique.const cart = ["Cahier A4"];
const copiedCart = [...cart];
// Choses séparées, donc === vaut false
console.log(cart === copiedCart); // false
// Même avec un contenu identique, toujours false
console.log(["A"] === ["A"]); // false
// Compare le contenu lui-même, et c'est true
console.log(cart[0] === copiedCart[0]); // true
// True seulement quand ils pointent sur la même chose
const savedCart = cart;
console.log(cart === savedCart); // true
La copie n'atteint pas l'intérieur d'une valeur imbriquée — copie superficielle vs profonde
La syntaxe spread, jusqu'ici, a fait des copies sûres pour des valeurs primitives comme un montant. Mais copie une commande avec un objet imbriqué, comme les infos client, et tandis que l'objet extérieur devient un objet séparé, celui de l'intérieur pointe toujours sur exactement la même chose que l'original. Corrige une adresse sur ce que tu pensais être la copie, et l'adresse de la commande originale change aussi — un décalage auquel tu ne t'attendais pas.
Ce que construit la syntaxe spread est une copie superficielle (une copie qui ne duplique que le premier niveau de propriétés, laissant tout objet imbriqué pointer sur la même chose). Les valeurs du premier niveau deviennent indépendantes si ce sont des primitives, mais quand ce qui est à l'intérieur est un objet ou un tableau, l'information de localisation — la référence — se fait juste copier telle quelle.
Quand tu as besoin que l'intérieur soit reconstruit aussi, utilise une copie profonde (une copie qui reconstruit tout, jusque dans les valeurs imbriquées). Écris structuredClone(order), et chaque niveau d'imbrication se fait reconstruire comme un objet séparé.
C'est une fonction que le navigateur fournit, donc ça marche directement dans la console de ce site aussi. Ça ne marche juste pas sur un objet qui contient une fonction.
const order = {
orderId: "ORD-1342",
customer: { name: "Kenji Ross", city: "Lyon" },
};
// Copie superficielle : le 1er niveau est séparé, l'intérieur pointe toujours sur la même chose
const shallow = { ...order };
shallow.orderId = "ORD-9999";
console.log(order.orderId); // ORD-1342 (indépendant)
shallow.customer.city = "Nantes";
console.log(order.customer.city); // Nantes (l'original a changé aussi)
console.log(order.customer === shallow.customer); // true
// Copie profonde : même l'intérieur devient séparé
const deep = structuredClone(order);
deep.customer.city = "Rennes";
console.log(order.customer.city); // Nantes (l'original est intact)
console.log(order.customer === deep.customer); // false
Empêcher les réécritures — Object.freeze
Comme tu l'as vu, les tableaux et objets peuvent finir modifiés sans le vouloir, juste en partageant une référence. Certaines valeurs, comme des paramètres par défaut ou un taux de taxe, demandent une protection différente de const — tu ne veux pas qu'elles soient réécrites plus tard. const seul n'empêche pas le contenu d'être réécrit, donc tu as besoin d'un moyen de marquer l'objet lui-même comme interdit aux changements.
Écris Object.freeze(settings), et cet objet arrête d'accepter des changements, ajouts, ou retraits sur ses propriétés. Essaie d'assigner, ajouter, ou supprimer quelque chose après l'avoir gelé, et ça lève un TypeError directement là, arrêtant l'exécution.
Vérifie si quelque chose est gelé avec Object.isFrozen(settings). Geler n'atteint que le premier niveau, donc un objet imbriqué doit être gelé séparément.
const settings = { theme: "dark" };
// Réécrivable avant le gel
settings.theme = "light";
console.log(settings.theme); // light
Object.freeze(settings);
console.log(Object.isFrozen(settings)); // true
// Assigner après le gel s'arrête sur cette ligne
// settings.theme = "dark";
// TypeError: Cannot assign to read only property 'theme' of object '#<Object>'
// Le gel n'atteint que le 1er niveau — l'intérieur ne s'arrête pas
const nested = Object.freeze({ display: { theme: "dark" } });
nested.display.theme = "light";
console.log(nested.display.theme); // light
// Pour le changer, construis un nouvel objet à la place
const updated = { ...settings, theme: "dark" };
console.log(updated.theme); // dark
console.log(settings.theme); // light (l'original est intact)
Pour le changer, construis un nouvel objet
Assigner, ajouter, ou supprimer sur un objet gelé lève un TypeError, et rien après cette ligne ne s'exécute. Pour changer un objet gelé, ne le réécris pas — construis-en un nouveau à la place, comme dans { ...settings, theme: "light" }.
Vérification des connaissances
Répondez à chaque question une par une.
Question 2Avec const a = ["A"]; const b = ["A"];, que renvoie a === b ?
Question 3Copie des données avec un objet imbriqué en utilisant { ...order }. Que se passe-t-il pour l'objet imbriqué ?