Retour au cours

frontend / javascript

Objets : destructuring et spread

Leçon 41 exercice

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.

TechniqueProfondeur copiéeFonctions/Symboles
{ ...objet }Premier niveau seulementIgnorés silencieusement s'ils sont imbriqués
JSON.parse(JSON.stringify(objet))Toute la profondeurPerdus (fonctions, undefined, dates deviennent des chaînes)
structuredClone(objet)Toute la profondeurFonctions 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.

js
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

1 disponible
1

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 :

js
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.

Résoudre l’exercice →