Aller au contenu
Réussir mon BTS SIO

Tape une notion, une commande ou le nom d'une épreuve.

Par exemple

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 Map et un Set ;
  • 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

js
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é :

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

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

js
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(); donne TypeError: Cannot read properties of undefined (reading 'nom'), parce que this vaut undefined dans 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 courte presenter() { … }.

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

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

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

js
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 » :

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

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

js
commande.client.adresse?.ville ?? 'adresse inconnue';   // 'adresse inconnue'

L'opérateur ??= donne une valeur à une propriété seulement si elle est null ou undefined :

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

js
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 JavaScriptDevient en JSON
undefined, une fonction (dans un objet)Propriété supprimée
undefined, une fonction (dans un tableau)null
NaN, Infinitynull
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.parseMessage (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 HTMLUnexpected 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.

js
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) :

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

js
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ésChaînes (et symboles)N'importe quelle valeur, objets compris
Nombre d'élémentsObject.keys(o).lengthmap.size
ParcoursObject.entries(o)for…of directement
Conversion en JSONDirectePasser par Object.fromEntries(map)
Usage typiqueDé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.

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

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

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

js
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, et getMonth() renvoie 10 pour novembre. Les jours de la semaine aussi : getDay() renvoie 0 pour dimanche, 1 pour 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 que new 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 :

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

  1. affichent le prix ;
  2. ajoutent une propriété fournisseur qui vaut 'Distri Pro' ;
  3. créent p2, une copie de p dont la quantité vaut 5, sans modifier p ;
  4. récupèrent ref et prix dans deux variables, en une ligne ;
  5. affichent toutes les propriétés de p au format clé = valeur.
Voir la réponseMasquer la réponse de l'exercice 1
js
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.

  1. {"ref": "RAM-16", "quantite": 3}
  2. {ref: "RAM-16"}
  3. ["SSD-512", "RAM-16",]
  4. {"actif": true, "fournisseur": null}
  5. {'ref': 'RAM-16'}
  6. 42
Voir la réponseMasquer la réponse de l'exercice 2
  1. Valide.
  2. Invalide : la clé ref doit être entre guillemets doubles.
  3. Invalide : virgule après le dernier élément.
  4. Valide : true et null sont des valeurs JSON.
  5. Invalide : JSON n'accepte que les guillemets doubles.
  6. Valide : un nombre seul est un document JSON (JSON.parse('42') renvoie 42).

Tu avais trouvé ?

Exercice 3 sur 5 Ce que perd JSON Réussi À revoir

Qu'affiche ce code ?

js
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

  1. À partir de pieces, écris l'expression qui donne la liste des catégories sans doublon, triée en français.
  2. Les techniciens ont scanné ces références pendant l'inventaire : ['SSD-512', 'RAM-16', 'SSD-512', 'PAT-01', 'SSD-512']. Construis une Map qui donne le nombre de scans par référence, et indique ce que vaut scans.get('SSD-512').
Voir la réponseMasquer la réponse de l'exercice 4
js
// 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
js
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é ?

Chapitre terminé !

Fais les 5 exercices avant de passer à la suite : c'est en t'entraînant que tout se fixe.

chapitres
–
de série
–
du cours
–
Chapitre suivant : Le DOM : querySelector, textContent et innerHTMLRevoir le sommaire du cours