frontend / javascript
Objets : destructuring et spread
Explication
Ce que vous allez apprendre
- Extraire des champs d'un objet avec le destructuring, y compris renommage et valeurs par défaut
- Créer une copie mise à jour d'un objet avec le spread (
...) sans muter l'original - Expliquer pourquoi le spread ne fait qu'une copie superficielle et quand ça pose problème
- Utiliser
structuredClone()pour obtenir une copie profonde fiable - Justifier pourquoi deux objets au contenu identique ne sont jamais
===en JavaScript
Dans quel contexte ?
Un développeur corrige un bug dans un formulaire d'édition de profil : quand on modifie l'adresse d'un utilisateur dans le formulaire, l'adresse de l'utilisateur affiché dans la liste juste à côté change aussi, sans qu'on ait rien demandé. La cause : le code fait const brouillon = { ...utilisateurOriginal } en pensant obtenir une copie indépendante, mais brouillon.adresse pointe encore vers le même objet adresse que l'original, puisque le spread ne copie que le premier niveau. Corriger ça demande soit une copie profonde, soit un spread appliqué aussi à adresse.
1. Un objet, une fiche avec des champs nommés
Un objet regroupe des informations liées sous forme de paires clé-valeur, un peu comme une fiche avec des champs nommés. Avant toute syntaxe avancée, retenez juste qu'on accède normalement à un champ avec objet.propriete.
2. Extraire des champs sans répéter "objet.propriete"
Le destructuring permet d'extraire directement certains champs dans des variables, en une seule fois. C'est particulièrement précieux dans les paramètres de fonction, où l'on documente ainsi, au premier coup d'œil, quelles propriétés une fonction attend réellement.
3. L'opération inverse : fusionner avec spread
Le spread (...) fait l'inverse du destructuring : il déplie le contenu d'un objet pour le fusionner dans un autre, sans jamais le modifier. C'est devenu le réflexe standard pour "mettre à jour" un objet en JavaScript, puisque le langage privilégie créer une nouvelle version plutôt que modifier l'ancienne.
4. Le piège à connaître absolument
Voici le point le plus important de la leçon : le spread ne copie que le premier niveau (copie "superficielle"). Si un objet contient un autre objet imbriqué, cette partie imbriquée reste partagée entre l'original et la copie — modifier l'un modifie l'autre.
Piège fréquent
{ ...utilisateur, adresse: utilisateur.adresse } ne protège pas la sous-propriété adresse : elle reste la même référence dans les deux objets. Pour modifier l'adresse sans affecter l'original, il faut aussi la spreader : { ...utilisateur, adresse: { ...utilisateur.adresse, ville: "Lyon" } }.
5. La solution : la copie profonde
Pour une copie totalement indépendante, il faut une copie "profonde", que structuredClone(), une fonction native du navigateur et de Node.js, fournit directement sans bibliothèque externe.
| Technique | Profondeur copiée | Fonctions/Symboles |
|---|---|---|
{ ...objet } | Premier niveau seulement | Ignorés silencieusement s'ils sont imbriqués |
JSON.parse(JSON.stringify(objet)) | Toute la profondeur | Perdus (fonctions, undefined, dates deviennent des chaînes) |
structuredClone(objet) | Toute la profondeur | Fonctions non supportées (erreur), dates et Map/Set préservés |
6. Un dernier réflexe à acquérir
Retenez enfin qu'en JavaScript, deux objets ne sont jamais considérés égaux (===) simplement parce qu'ils ont le même contenu : l'égalité porte sur la référence en mémoire, pas sur la structure. Cette leçon complète celle sur les tableaux : ensemble, destructuring et spread forment le vocabulaire quotidien de la manipulation de données en JavaScript moderne.
Commandes & code
Objets : destructuring et spread
Manipuler des structures clé-valeur de façon expressive.
const utilisateur = {
nom: "Alice",
age: 30,
adresse: { ville: "Paris", codePostal: "75001" },
};
// Destructuring de base, avec renommage et valeurs par defaut
const { nom, age: ageUtilisateur, pays = "France" } = utilisateur;
// Destructuring imbrique
const { adresse: { ville, codePostal } } = utilisateur;
// Destructuring dans les parametres de fonction (tres courant en React/Node)
function afficherProfil({ nom, age, adresse: { ville } = {} }) {
console.log(`${nom}, ${age} ans, ${ville}`);
}
afficherProfil(utilisateur);
// Spread : copier/fusionner des objets sans mutation
const utilisateurMisAJour = { ...utilisateur, age: 31 }; // shallow copy + override
const fusion = { ...utilisateur, ...{ theme: "sombre" } };
console.log(utilisateur.age); // 30 -- l'original n'est PAS modifie
// ATTENTION : le spread est une copie SUPERFICIELLE (shallow)
const copieSuperficielle = { ...utilisateur };
copieSuperficielle.adresse.ville = "Lyon";
console.log(utilisateur.adresse.ville); // "Lyon" -- impacte, car adresse est partagee !
// Copie profonde : structuredClone (natif, moderne) ou JSON (avec limites)
const copieProfonde = structuredClone(utilisateur);
copieProfonde.adresse.ville = "Marseille";
console.log(utilisateur.adresse.ville); // toujours "Lyon", non impacte
// Rest operator sur un objet : extraire certaines cles, garder le reste
const { nom: nomExtrait, ...proprietesRestantes } = utilisateur;
console.log(proprietesRestantes); // { age: 30, adresse: {...} }
// Object.entries / keys / values : passer d'un objet a des structures iterables
console.log(Object.keys(utilisateur));
console.log(Object.values(utilisateur));
console.log(Object.entries(utilisateur));
for (const [cle, valeur] of Object.entries(utilisateur)) {
console.log(`${cle}: ${JSON.stringify(valeur)}`);
}
// Object.fromEntries : reconstruire un objet depuis des paires
const doubleValeurs = Object.fromEntries(
Object.entries({ a: 1, b: 2 }).map(([cle, val]) => [cle, val * 2])
);
// Computed property names : cles dynamiques
const cleDynamique = "statut";
const objet = { [cleDynamique]: "actif", [`${cleDynamique}_depuis`]: "2024" };
// Object.freeze / Object.seal : immuabilite (superficielle uniquement)
const constante = Object.freeze({ pi: 3.14159 });
constante.pi = 4; // ignore silencieusement (ou erreur en mode strict)
console.log(constante.pi); // 3.14159 toujours
// Property shorthand
function creerPoint(x, y) {
return { x, y }; // equivalent a { x: x, y: y }
}
// Getters/setters dans un objet litteral
const compte = {
_solde: 100,
get solde() {
return `${this._solde} EUR`;
},
set solde(valeur) {
if (valeur < 0) throw new Error("Solde negatif interdit");
this._solde = valeur;
},
};
console.log(compte.solde);
compte.solde = 200;
// Object.assign : alternative pre-spread, mute le premier argument
const cible = { a: 1 };
Object.assign(cible, { b: 2 }, { c: 3 });
console.log(cible); // { a: 1, b: 2, c: 3 }
// Comparaison d'objets : reference, pas structure
console.log({ a: 1 } === { a: 1 }); // false : deux objets differents en memoire
console.log(JSON.stringify({ a: 1 }) === JSON.stringify({ a: 1 })); // true (comparaison textuelle)Résumé
- Le spread (
{...obj}) est une copie superficielle : les objets imbriqués restent partagés. structuredClone()est l'API native moderne pour une copie profonde fiable.Object.freeze()empêche la modification de premier niveau uniquement (pas récursif).- Deux objets ne sont jamais
===sauf s'ils référencent le même emplacement mémoire.
Exercices pratiques
Mission : le brouillon de profil qui contamine l'original
Objectif : Diagnostiquer et corriger un bug de copie superficielle sur un objet imbriqué, puis choisir la bonne technique de copie selon le contexte.
Contexte
Sur la page d'édition de profil de Technologik, un utilisateur modifie la ville de son adresse dans un formulaire de brouillon, et remarque que la fiche affichée juste à côté (censée montrer les données non enregistrées) change en même temps. Le code source :
const utilisateurOriginal = { nom: "Alice", adresse: { ville: "Paris", codePostal: "75001" } };
const brouillon = { ...utilisateurOriginal };
brouillon.adresse.ville = "Lyon";Tu dois expliquer précisément pourquoi ça arrive, corriger le code, et choisir la bonne technique de copie pour un objet dont la profondeur n'est pas connue à l'avance.