Objets et références
Comprendre pourquoi deux variables peuvent partager le même objet.
Objectifs
À la fin de cette leçon, vous saurez :
- expliquer mécaniquement pourquoi modifier
bchange aussiaquand ils partagent un objet ; - distinguer mutation et réaffectation, et prédire l'effet de chacune sous
const; - tester l'identité de deux objets avec
===, et savoir comparer des contenus à la main ; - appliquer tout cela aux tableaux, qui suivent exactement la même règle.
🔗 Pour vous rafraîchir la mémoire : la copie par valeur des primitives
L'observation qui surprend
const a = { name: "Paul" };
const b = a; // « copie » ?
b.name = "Jean";
console.log(a.name); // "Jean" ← on n'a touché qu'à b !
Après la leçon précédente, ce résultat devrait vous choquer : une copie modifiée qui change l'original ? La résolution du mystère tient en une phrase : b n'est pas une copie de l'objet — c'est une copie de son adresse.
Une variable d'objet ne contient pas l'objet
Un objet est trop gros (et trop évolutif) pour tenir dans une variable. Il vit dans une zone de mémoire dédiée ; la variable contient seulement son adresse — la fameuse référence. Reprenons la séquence complète :
Étape 1 : const a = { name: "Paul" };
a ──────→ { name: "Paul" } l'objet vit ailleurs ;
a contient juste son adresse
Étape 2 : const b = a;
a ──────→ { name: "Paul" }
↑ l'ADRESSE est copiée :
b ─────────────┘ deux variables, UN SEUL objet
Étape 3 : b.name = "Jean"; mutation de l'unique objet
a ──────→ { name: "Jean" }
↑
b ─────────────┘ les deux voient la modification
L'image mentale utile, opposée aux photocopies de la leçon précédente : la référence est une adresse postale. Copier l'adresse sur un second bout de papier ne duplique pas la maison : les deux papiers mènent à la même porte, et quiconque y entre pour repeindre un mur change la maison vue par tous.
Vérification en conditions réelles :
> const a = { name: "Paul" }
undefined
> const b = a
undefined
> b.name = "Jean"
'Jean'
> a
{ name: 'Jean' } ← confirmé : un seul objet derrière deux noms
Mutation vs réaffectation : la distinction qui débloque tout
Deux gestes radicalement différents, souvent confondus :
const user = { name: "Paul" };
user.name = "Jean"; // MUTATION : je modifie L'OBJET pointé
user = {}; // TypeError : RÉAFFECTATION interdite par const
- Mutation (
obj.prop = ...,array.push(...)) : on entre dans la maison et on la transforme. La référence ne bouge pas ; toutes les variables pointant cet objet voient le changement. - Réaffectation (
variable = autreChose) : on colle une nouvelle adresse sur le papier. L'ancienne maison reste intacte ; seules les variables réaffectées changent de cible.
Et maintenant, le mystère du chapitre 3 s'éclaircit enfin :
const notes = [12];
notes.push(15); // AUTORISÉ : mutation → la référence n'a pas changé
notes = []; // TypeError : réaffectation → nouvelle adresse refusée
const verrouille la variable — c'est-à-dire la case qui contient l'adresse. Il ne verrouille pas l'objet visé. Formule à retenir : « const protège la flèche, jamais la cible. »
Tester l'identité : === compare des adresses
L'égalité stricte, appliquée aux objets, compare les références — jamais les contenus :
const x = { id: 1 };
const y = x; // même adresse que x
const z = { id: 1 }; // nouvel objet, contenu identique
console.log(x === y); // true : UNE seule maison, deux adresses identiques
console.log(x === z); // false : DEUX maisons, même décor
Cela peut surprendre (« mais ils sont pareils ! ») — c'est pourtant cohérent : comparer des contenus coûterait cher et serait ambigu (les propriétés dans quel ordre ? les méthodes ?). JavaScript tranche : === sur objets signifie strictement « même objet en mémoire ».
Pour comparer des contenus, il faut le faire explicitement, propriété par propriété :
function memesContenus(u1, u2) {
return u1.id === u2.id && u1.name === u2.name;
}
console.log(memesContenus(x, z)); // true : cette fois, le contenu
(Pour des structures profondes, on sérialise parfois en JSON — sujet abordé plus loin dans le cursus, avec ses pièges.)
Conséquence pratique très concrète : pour détecter si deux variables parlent du même objet (utile avant une mutation risquée), === est exactement l'outil :
if (copie !== original) {
// je peux muter copie sans affecter original...
}
Les tableaux : même loi, mêmes surprises
Tout ce qui n'est pas primitif suit la règle des références — tableaux compris :
const original = ["a", "b"];
const alias = original; // copie de la RÉFÉRENCE
alias.push("c");
console.log(original.length); // 3 : le tableau « original » a grossi !
Déroulé mémoire :
original ────┐
├────→ [ "a", "b", "c" ] un seul tableau
alias ───────┘
Et la même asymétrie mutation/réaffectation :
let liste = ["a"];
let autre = liste;
autre.push("b"); // mutation → visible partout
autre = ["z"]; // réaffectation → seule « autre » change
console.log(liste); // [ 'a', 'b' ]
console.log(autre); // [ 'z' ]
Si votre intuition a tiqué devant liste affichant ['a','b'] alors que c'est autre qu'on a « modifié » : bravo, c'est exactement la mécanique. Le push a touché l'objet partagé ; le = n'a retourné qu'une adresse locale.
Exercice
Prédisez chaque résultat sans exécuter, notez, puis vérifiez.
const config = { theme: "sombre" };
const prefs = config;
prefs.theme = "clair";
console.log(config.theme);
const u1 = { id: 1 };
const u2 = { id: 1 };
console.log(u1 === u2);
console.log(u1.id === u2.id);
let liste = ["a"];
let autreListe = liste;
autreListe = ["b"]; // réaffectation !
console.log(liste[0]);
-
Écrivez
memeObjet(a, b)qui retournetrueuniquement si les deux paramètres désignent le même objet — pas le même contenu. -
Écrivez
compteCommuns(t1, t2)qui reçoit deux tableaux et retourne combien d'éléments ils partagent par référence (indice : double boucle +===). Testez avec["x","y"]et[t1[0], "z"]. -
Expliquez pourquoi
const notes = []; notes.push(1)passe alors quenotes = [1]échoue — en une phrase utilisant les mots « flèche » et « cible ».
Résumé
- Une variable d'objet contient une référence (adresse), pas l'objet.
- Copier la référence partage l'objet : toute mutation est visible par tous les porteurs.
- Mutation = transformer la cible ; réaffectation = changer la flèche.
constbloque la seconde seulement. ===sur objets teste l'identité d'adresse ; les contenus se comparent à la main.- Tableaux : même loi, donc
pushsur un tableau « copié » modifie l'original.
Correction disponibleCherchez d’abord par vous-même.Voir la correction
Correction
Réponses détaillées
Extrait 1. Affiche "clair". prefs a reçu la copie de l'adresse ; une seule maison existe, repeinte par prefs, donc vue « clair » par config.
Extrait 2. Affiche false puis true. Première ligne : deux objets distincts (deux créations = deux adresses), donc === répond faux malgré le contenu identique. Seconde ligne : .id extrait une primitive (1) de chaque côté, et là, comparaison par valeur → vrai. Même opérateur, deux familles de données, deux règles.
Extrait 3. Affiche "a". autreListe = ["b"] est une réaffectation : elle colle une nouvelle adresse sur le papier autreListe, sans toucher au tableau pointé par liste. Si la ligne avait été autreListe.push("b") (mutation), liste aurait affiché les deux éléments.
Question 4.
function memeObjet(a, b) {
return a === b;
}
Rien d'autre à faire : === répond précisément à la question « même adresse ? ». C'est le cas rare où la réponse tient en un opérateur — parce que la question était bien posée.
Question 5.
function compteCommuns(t1, t2) {
let compteur = 0;
for (const e1 of t1) {
for (const e2 of t2) {
if (e1 === e2) {
compteur++;
}
}
}
return compteur;
}
const base = ["x", "y"];
console.log(compteCommuns(base, [base[0], "z"])); // 1
Le test fonctionne car base[0] recopie la référence du premier élément : elle est égale par adresse à celle contenue dans base. Attention à la limite pédagogique : ici les éléments sont des chaînes (primitives), donc === compare des valeurs — le test mesure des valeurs communes, pas des références. Avec des objets en éléments, il mesurerait l'identité. Savoir laquelle des deux règles s'applique selon le type des éléments est précisément la compétence visée par ce chapitre.
Question 6. Réponse attendue : « const verrouille la flèche (l'adresse stockée dans la variable), pas la cible (l'objet) ; push transforme la cible — permis ; remplace la flèche — interdit. »