Chapitre 621 minDébutant
Objets, JSON, Map et Set en JavaScript
Par Josselyn C., prof de BTS SIO. Mis à jour le .
À la fin de ce chapitre, tu sauras
- créer, lire, modifier et parcourir un objet ;
- copier un objet sans modifier l'original ;
- convertir des données en JSON et lire du JSON ;
- choisir entre un objet, une
Mapet unSet; - afficher des prix et des dates en français avec
Intl.
5 exercices corrigés
Un objet JavaScript est un ensemble de paires clé-valeur, écrit entre accolades, qui décrit une chose : une pièce, un client, une commande. JSON est le format texte qui reprend cette écriture pour échanger des données entre applications. Ce chapitre montre comment créer, lire, copier et parcourir un objet, comment passer d'un objet au JSON et inversement, quand préférer une Map ou un Set, et comment afficher des prix et des dates en français avec Intl.
Créer un objet et lire ses propriétés
const piece = {
ref: 'SSD-512',
nom: 'SSD NVMe 512 Go',
categorie: 'Stockage',
quantite: 12,
seuil: 5,
prix: 42.9,
};
Chaque ligne est une propriété : une clé (ref), deux-points, une valeur. Les clés sont des chaînes, écrites sans guillemets quand ce sont des noms valides. Deux façons de lire une propriété :
piece.nom // 'SSD NVMe 512 Go' : notation pointée, la plus courante
piece['prix'] // 42.9 : notation entre crochets
const champ = 'quantite';
piece[champ] // 12 : les crochets acceptent une variable
piece.fournisseur // undefined : propriété absente, pas d'erreur
Les crochets sont indispensables quand le nom de la propriété est dans une variable (par exemple le name d'un champ de formulaire) ou n'est pas un nom valide (piece['date-achat']).
Ajouter, modifier, supprimer une propriété
piece.emplacement = 'Étagère B2'; // ajoute la propriété
piece.quantite -= 1; // modifie : 11
delete piece.emplacement; // supprime
'emplacement' in piece // false
Object.hasOwn(piece, 'ref') // true
Un objet déclaré avec const reste modifiable : const empêche seulement de faire désigner à la variable un autre objet (piece = {} provoque une erreur).
Les méthodes et this
Une propriété dont la valeur est une fonction s'appelle une méthode. Dans une méthode, this désigne l'objet sur lequel elle est appelée :
const atelier = {
nom: 'Réparinfo',
techniciens: ['Julie', 'Karim', 'Sofiane'],
presenter() {
return `${this.nom} : ${this.techniciens.length} techniciens`;
},
};
atelier.presenter(); // 'Réparinfo : 3 techniciens'
this dépend de la façon d'appeler la fonction, pas de l'endroit où elle est écrite. Deux pièges :
- Une méthode copiée dans une variable perd son objet :
const f = atelier.presenter; f();donneTypeError: Cannot read properties of undefined (reading 'nom'), parce quethisvautundefineddans un module. - Une fonction fléchée n'a pas de
thisà elle : écrite comme méthode (presenter: () => this.nom), elle ne voit pas l'objet. Pour une méthode, utilise la syntaxe courtepresenter() { … }.
Tu retrouveras this avec les classes au chapitre 11. Dans une application en JavaScript moderne, tu manipules surtout des objets de données (sans méthodes), traités par des fonctions : c'est le style du fil rouge.
Parcourir un objet : Object.keys, values et entries
Object.keys(piece); // ['ref', 'nom', 'categorie', 'quantite', 'seuil', 'prix']
Object.values({ a: 1, b: 2 }); // [1, 2]
Object.entries({ ref: 'X', quantite: 2 }); // [['ref', 'X'], ['quantite', 2]]
for (const [cle, valeur] of Object.entries({ ref: 'RAM-16', quantite: 3 })) {
console.log(`${cle} : ${valeur}`);
}
// ref : RAM-16
// quantite : 3
Ces trois fonctions renvoient des tableaux : tu peux enchaîner map, filter ou reduce. Object.fromEntries fait l'inverse d'Object.entries : elle fabrique un objet à partir d'une liste de paires. Tu t'en serviras pour lire un formulaire (chapitre 8).
Copier un objet sans modifier l'original
Une variable ne contient pas l'objet, mais une référence vers lui. Affecter un objet à une autre variable ne le copie pas :
const reference = piece;
reference.quantite = 0;
console.log(piece.quantite); // 0 : c'est le même objet
Pour copier, utilise la décomposition ... (spread) dans un nouvel objet. Les propriétés écrites après écrasent celles de la copie :
const copie = { ...piece, quantite: 11 };
console.log(piece.quantite, copie.quantite); // 12 11
const fusion = { ...{ a: 1, b: 2 }, ...{ b: 3, c: 4 } }; // { a: 1, b: 3, c: 4 }
Décomposer un objet (destructuring)
La déstructuration range des propriétés dans des variables du même nom. Tu peux renommer, donner une valeur par défaut et récupérer « le reste » :
const { ref, quantite, prix: prixUnitaire, fournisseur = 'inconnu', ...reste } = piece;
// ref = 'SSD-512', quantite = 12, prixUnitaire = 42.9, fournisseur = 'inconnu'
// reste = { nom: 'SSD NVMe 512 Go', categorie: 'Stockage', seuil: 5 }
C'est la même syntaxe que dans les paramètres d'une fonction (function etiquette({ ref, nom }), chapitre 4) et dans les modules (import { valeurStock } from './stock.js').
Le chaînage optionnel ?. et la coalescence ??
Lire une propriété d'une valeur undefined ou null arrête le script. Le chaînage optionnel ?. s'arrête proprement et renvoie undefined si ce qui est à sa gauche est null ou undefined :
const commande = { client: { nom: 'Lefèvre', adresse: null }, lignes: [] };
commande.livreur.nom; // TypeError: Cannot read properties of undefined (reading 'nom')
commande.livreur?.nom; // undefined
commande.client.adresse?.ville; // undefined
commande.lignes[0]?.ref; // undefined
commande.client.nom?.toUpperCase(); // 'LEFÈVRE'
Combine-le avec ?? pour afficher une valeur par défaut :
commande.client.adresse?.ville ?? 'adresse inconnue'; // 'adresse inconnue'
L'opérateur ??= donne une valeur à une propriété seulement si elle est null ou undefined :
const options = { delai: 0 };
options.delai ??= 5; // reste 0
options.essais ??= 3; // ajoutée : 3
JSON : JSON.stringify et JSON.parse
JSON (JavaScript Object Notation) est un format texte d'échange de données, normalisé (RFC 8259), que lisent tous les langages. C'est le format des API REST, de beaucoup de fichiers de configuration (package.json) et de ce que tu ranges dans localStorage. Sa syntaxe vient des objets JavaScript, en plus strict :
| Règle JSON | Exemple valide | Invalide |
|---|---|---|
| Clés entre guillemets doubles | {"ref": "RAM-16"} | {ref: "RAM-16"}, {'ref': 'RAM-16'} |
| Chaînes entre guillemets doubles | "Écran" | 'Écran' |
| Pas de virgule après le dernier élément | [1, 2] | [1, 2,] |
Valeurs : chaîne, nombre, true, false, null, objet, tableau | {"stock": null} | undefined, fonctions, NaN, dates |
| Pas de commentaires | // … |
JSON.stringify transforme une valeur JavaScript en texte JSON ; JSON.parse fait l'inverse.
const texte = JSON.stringify({ ref: 'RAM-16', quantite: 3, prix: 38.5 });
console.log(texte); // '{"ref":"RAM-16","quantite":3,"prix":38.5}'
typeof texte; // 'string'
const objet = JSON.parse(texte);
objet.prix + 1; // 39.5 : un vrai nombre à nouveau
JSON.stringify({ ref: 'X', quantite: 1 }, null, 2); // texte indenté de 2 espaces, lisible
JSON.stringify ne garde que ce que JSON sait représenter. Le reste est transformé ou perdu sans prévenir :
| Valeur JavaScript | Devient en JSON |
|---|---|
undefined, une fonction (dans un objet) | Propriété supprimée |
undefined, une fonction (dans un tableau) | null |
NaN, Infinity | null |
new Date(…) | Une chaîne ISO : "2026-11-02T09:30:00.000Z" |
new Map(…), new Set(…) | {} : contenu perdu |
Une date relue avec JSON.parse reste donc une chaîne : recrée-la avec new Date(chaine) si tu dois calculer avec.
JSON.parse lève une SyntaxError dès que le texte n'est pas du JSON valide. C'est fréquent avec des données qui viennent de l'extérieur, d'où le try…catch obligatoire :
Texte passé à JSON.parse | Message (Chrome, Edge, Node.js) |
|---|---|
{'ref': 'X'} | Expected property name or '}' in JSON at position 1 |
{"ref": "X",} | Expected double-quoted property name in JSON at position 12 |
'' (chaîne vide) | Unexpected end of JSON input |
undefined | "undefined" is not valid JSON |
| Une page HTML | Unexpected token '<', "<!doctype "... is not valid JSON |
Le dernier cas est un classique : ton code attend du JSON, mais le serveur a renvoyé une page d'erreur HTML (chapitre 10). Le format JSON et sa comparaison avec XML et CSV sont détaillés dans le chapitre JSON, XML et CSV du cours API REST.
Set : une collection sans doublons
Un Set est un ensemble de valeurs uniques. Ajouter une valeur déjà présente ne fait rien.
const categories = new Set(['Stockage', 'Mémoire', 'Stockage']);
categories.add('Écran');
categories.add('Mémoire'); // déjà présente : ignorée
categories.size; // 3
categories.has('Écran'); // true
categories.delete('Écran');
[...categories]; // ['Stockage', 'Mémoire']
// L'usage le plus courant : dédoublonner un tableau
[...new Set(['SSD-512', 'RAM-16', 'SSD-512'])]; // ['SSD-512', 'RAM-16']
Depuis ECMAScript 2025, les Set ont les opérations ensemblistes que tu connais en mathématiques (cours Ensembles) :
const lundi = new Set(['SSD-512', 'RAM-16', 'PAT-01']); // pièces sorties lundi
const mardi = new Set(['RAM-16', 'CHG-65']); // pièces sorties mardi
[...lundi.union(mardi)]; // ['SSD-512', 'RAM-16', 'PAT-01', 'CHG-65']
[...lundi.intersection(mardi)]; // ['RAM-16'] : sorties les deux jours
[...lundi.difference(mardi)]; // ['SSD-512', 'PAT-01'] : lundi mais pas mardi
Map : un dictionnaire clé-valeur
Une Map associe des clés à des valeurs, comme un objet, avec des différences utiles quand le contenu change souvent :
const stock = new Map([
['SSD-512', 12],
['RAM-16', 3],
]);
stock.set('CHG-65', 0);
stock.set('RAM-16', stock.get('RAM-16') + 2);
stock.get('RAM-16'); // 5
stock.get('XXX-00'); // undefined
stock.has('XXX-00'); // false
stock.size; // 3
for (const [ref, quantite] of stock) {
console.log(ref, quantite); // dans l'ordre d'ajout
}
Objet {} | Map | |
|---|---|---|
| Type des clés | Chaînes (et symboles) | N'importe quelle valeur, objets compris |
| Nombre d'éléments | Object.keys(o).length | map.size |
| Parcours | Object.entries(o) | for…of directement |
| Conversion en JSON | Directe | Passer par Object.fromEntries(map) |
| Usage typique | Décrire une chose (une pièce) | Un index, un cache, un comptage qui évolue |
Un usage fréquent : construire un index pour retrouver une pièce par sa référence sans parcourir tout le tableau à chaque fois.
const index = new Map(pieces.map((p) => [p.ref, p]));
index.get('ECR-156').nom; // 'Dalle 15,6 pouces'
Itérables et itérateurs
Un itérable est une valeur que for…of et l'opérateur ... savent parcourir : les tableaux, les chaînes, les Map et les Set le sont, un objet ordinaire {} ne l'est pas. Les méthodes keys(), values() et entries() de ces collections renvoient un itérateur : un curseur qui fournit les valeurs une par une, et pas un tableau.
const refs = stock.keys();
Array.isArray(refs); // false
refs.length; // undefined : un itérateur n'a ni longueur ni indice
[...refs]; // ['SSD-512', 'RAM-16', 'CHG-65']
[...refs]; // [] : un itérateur ne se parcourt qu'une fois
Array.from(stock.values()).toSorted((a, b) => a - b); // [0, 5, 12]
C'est pourquoi pieces.entries(), utilisé au chapitre 3 dans for (const [i, p] of pieces.entries()), marche dans une boucle mais ne s'affiche pas comme un tableau. Pour trier, compter ou lire un élément par son indice, convertis d'abord l'itérateur avec [...iterateur] ou Array.from(iterateur). Depuis 2025, les navigateurs actuels donnent aussi aux itérateurs leurs propres méthodes map, filter et toArray ; la conversion en tableau reste la façon la plus simple et la plus lisible.
Formater les prix et les dates avec Intl
Afficher 970.8 à un utilisateur français ne va pas : il attend 970,80 €. L'objet Intl (internationalisation), intégré au langage, formate nombres, monnaies, dates et listes selon la langue :
const euros = new Intl.NumberFormat('fr-FR', { style: 'currency', currency: 'EUR' });
euros.format(970.8); // '970,80 €'
euros.format(1040.9); // '1 040,90 €'
euros.format(6.5); // '6,50 €'
new Intl.NumberFormat('fr-FR').format(1234567.891); // '1 234 567,891'
new Intl.NumberFormat('fr-FR', { maximumFractionDigits: 1 }).format(3.14159); // '3,1'
new Intl.NumberFormat('fr-FR', { style: 'percent' }).format(0.2); // '20 %'
Crée le formateur une fois et réutilise-le : c'est plus rapide que d'appeler nombre.toLocaleString('fr-FR', …) à chaque ligne d'un tableau.
Pour les dates, l'objet Date stocke un instant ; Intl.DateTimeFormat l'affiche :
const d = new Date(2026, 10, 2, 14, 30); // 2 novembre 2026, 14 h 30 (heure locale)
new Intl.DateTimeFormat('fr-FR', { dateStyle: 'full' }).format(d); // 'lundi 2 novembre 2026'
new Intl.DateTimeFormat('fr-FR', { dateStyle: 'short' }).format(d); // '02/11/2026'
new Intl.DateTimeFormat('fr-FR', { dateStyle: 'medium', timeStyle: 'short' }).format(d);
// '2 nov. 2026, 14:30'
const relatif = new Intl.RelativeTimeFormat('fr', { numeric: 'auto' });
relatif.format(-1, 'day'); // 'hier'
relatif.format(2, 'week'); // 'dans 2 semaines'
new Intl.ListFormat('fr', { type: 'conjunction' }).format(['SSD-512', 'RAM-16', 'CHG-65']);
// 'SSD-512, RAM-16 et CHG-65'
L'objet Date a des pièges connus de tous les développeurs :
- Les mois commencent à 0 :
new Date(2026, 10, 2)est le 2 novembre, etgetMonth()renvoie10pour novembre. Les jours de la semaine aussi :getDay()renvoie0pour dimanche,1pour lundi. - Les dates débordent sans erreur : partir du 31 janvier 2026 et faire
setMonth(1)(février) donne le 3 mars, car le 31 février n'existe pas. - Fuseau horaire :
new Date('2026-11-02')est minuit UTC, alors quenew Date(2026, 10, 2)est minuit heure locale. En France, l'une affiche le 2 novembre à 1 h, l'autre à 0 h. - Différence de deux dates : la soustraction donne des millisecondes.
(fin - debut) / (1000 * 60 * 60 * 24)donne un nombre de jours.
Fil rouge : une pièce, son JSON et son affichage
Les pièces de Gestock sont des objets de données. Pour l'affichage, on prépare un formateur de prix, et pour l'enregistrement, on passe par JSON :
const euros = new Intl.NumberFormat('fr-FR', { style: 'currency', currency: 'EUR' });
function ligneTexte({ ref, nom, quantite, prix }) {
return `${ref} · ${nom} · ${quantite} en stock · ${euros.format(prix)}`;
}
console.log(ligneTexte(pieces[0]));
// SSD-512 · SSD NVMe 512 Go · 12 en stock · 42,90 €
const sauvegarde = JSON.stringify(pieces); // le texte à ranger dans localStorage
const relu = JSON.parse(sauvegarde); // un nouveau tableau d'objets, identique
console.log(relu.length, relu[2].ref); // 5 'CHG-65'
Exercices
Exercice 1 sur 5 Lire et modifier un objet Réussi À revoir
Avec const p = { ref: 'ECR-156', nom: 'Dalle 15,6 pouces', quantite: 2, seuil: 1, prix: 89 };, écris les instructions qui :
- affichent le prix ;
- ajoutent une propriété
fournisseurqui vaut'Distri Pro'; - créent
p2, une copie depdont la quantité vaut 5, sans modifierp; - récupèrent
refetprixdans deux variables, en une ligne ; - affichent toutes les propriétés de
pau formatclé = valeur.
Voir la réponseMasquer la réponse de l'exercice 1
console.log(p.prix); // 1. 89
p.fournisseur = 'Distri Pro'; // 2.
const p2 = { ...p, quantite: 5 }; // 3.
const { ref, prix } = p; // 4.
for (const [cle, valeur] of Object.entries(p)) {
console.log(`${cle} = ${valeur}`); // 5.
}
Tu avais trouvé ?
Exercice 2 sur 5 JSON valide ou non ? Réussi À revoir
Lesquels de ces textes sont du JSON valide ? Pour chaque texte invalide, dis ce qui ne va pas.
{"ref": "RAM-16", "quantite": 3}{ref: "RAM-16"}["SSD-512", "RAM-16",]{"actif": true, "fournisseur": null}{'ref': 'RAM-16'}42
Voir la réponseMasquer la réponse de l'exercice 2
- Valide.
- Invalide : la clé
refdoit être entre guillemets doubles. - Invalide : virgule après le dernier élément.
- Valide :
trueetnullsont des valeurs JSON. - Invalide : JSON n'accepte que les guillemets doubles.
- Valide : un nombre seul est un document JSON (
JSON.parse('42')renvoie42).
Tu avais trouvé ?
Exercice 3 sur 5 Ce que perd JSON Réussi À revoir
Qu'affiche ce code ?
const p = { ref: 'RAM-16', remise: undefined, prix: NaN, reception: new Date(Date.UTC(2026, 10, 2)) };
const relu = JSON.parse(JSON.stringify(p));
console.log(relu.remise, relu.prix, typeof relu.reception);
Voir la réponseMasquer la réponse de l'exercice 3
undefined null string.
La propriété remise (valeur undefined) a été supprimée par stringify, donc relu.remise vaut undefined. NaN est devenu null. La date est devenue la chaîne "2026-11-02T00:00:00.000Z" : il faut écrire new Date(relu.reception) pour retrouver une date.
Tu avais trouvé ?
Exercice 4 sur 5 Dédoublonner et compter Réussi À revoir
- À partir de
pieces, écris l'expression qui donne la liste des catégories sans doublon, triée en français. - Les techniciens ont scanné ces références pendant l'inventaire :
['SSD-512', 'RAM-16', 'SSD-512', 'PAT-01', 'SSD-512']. Construis uneMapqui donne le nombre de scans par référence, et indique ce que vautscans.get('SSD-512').
Voir la réponseMasquer la réponse de l'exercice 4
// 1.
[...new Set(pieces.map((p) => p.categorie))].toSorted((a, b) => a.localeCompare(b, 'fr'));
// ['Alimentation', 'Consommable', 'Écran', 'Mémoire', 'Stockage']
// 2.
const scans = new Map();
for (const ref of ['SSD-512', 'RAM-16', 'SSD-512', 'PAT-01', 'SSD-512']) {
scans.set(ref, (scans.get(ref) ?? 0) + 1);
}
scans.get('SSD-512'); // 3
Tu avais trouvé ?
Exercice 5 sur 5 Afficher en français Réussi À revoir
Écris une fonction resumeStock(pieces, date) qui renvoie une phrase de la forme Stock au lundi 2 novembre 2026 : 970,80 €, en utilisant Intl pour la date et le montant.
Voir la réponseMasquer la réponse de l'exercice 5
const euros = new Intl.NumberFormat('fr-FR', { style: 'currency', currency: 'EUR' });
const jourComplet = new Intl.DateTimeFormat('fr-FR', { dateStyle: 'full' });
function resumeStock(pieces, date) {
const valeur = pieces.reduce((total, p) => total + p.quantite * p.prix, 0);
return `Stock au ${jourComplet.format(date)} : ${euros.format(valeur)}`;
}
resumeStock(pieces, new Date(2026, 10, 2));
// 'Stock au lundi 2 novembre 2026 : 970,80 €'
Tu avais trouvé ?