Séance 11 — Les objets et les classes
Regrouper ce qui va ensemble
- Créer un objet et lire ses propriétés
- Parcourir un tableau d’objets
- Fabriquer des objets en série avec une classe
1. Le problème
À la fin de la séance 10, on stockait les inscrits comme ça :
const inscrits = ["Camille, 19 ans — L2 Informatique"];Une phrase. Impossible de trier par âge, de compter les L2, de changer le format d’affichage. L’information est là, mais elle est en bouillie.
Ce qu’on voudrait, c’est garder les morceaux séparés mais ensemble.
2. L’objet
const etudiant = {
prenom: "Camille",
age: 19,
formation: "L2 Informatique",
boursier: true,
};{
prenom : "Camille" ,
└──┬──┘ └───┬───┘ │
│ │ └── virgule entre chaque paire
│ └──────── la VALEUR (n'importe quel type)
└──────────────────── la PROPRIÉTÉ (le nom de l'information)
}
| Structure | Sert à | Repère |
|---|---|---|
Tableau [ ] |
Plusieurs choses du même genre | Accès par position : t[0] |
Objet { } |
Plusieurs informations sur une même chose | Accès par nom : o.prenom |
Lire et modifier
console.log(etudiant.prenom); // "Camille"
console.log(etudiant.age); // 19
etudiant.age = 20; // modifier
etudiant.ville = "Palaiseau"; // ajouter une propriétéetudiant.Prenom // ❌ undefined — majuscule : ce n'est pas la même propriété
etudiant["prenom"] // ✅ ça marche, mais on préfère le point
etudiant.prenom() // ❌ erreur — ce n'est pas une fonction, pas de parenthèsesQuand vous obtenez undefined sur un objet, la cause est presque toujours une faute de frappe dans le nom de la propriété. JavaScript ne prévient pas : il répond simplement « je n’ai rien à ce nom-là ».
Exercice 11.1
Créez un objet livre avec un titre, un auteur, une année et un booléen lu. Affichez une phrase de présentation avec un gabarit.
app.js
const livre = {
titre: "Le Petit Prince",
auteur: "Antoine de Saint-Exupéry",
annee: 1943,
lu: true,
};
console.log(`${livre.titre}, de ${livre.auteur} (${livre.annee})`);3. Un tableau d’objets
C’est la structure la plus utilisée du développement web. Retenez-la bien.
const etudiants = [
{ prenom: "Camille", age: 19, formation: "L2 Info" },
{ prenom: "Alex", age: 21, formation: "L3 Physique" },
{ prenom: "Sam", age: 18, formation: "L1 Sciences" },
];On la parcourt avec la boucle de la séance 8 :
for (const etudiant of etudiants) {
console.log(`${etudiant.prenom} a ${etudiant.age} ans`);
}Étape 1 — Afficher un tableau d’objets dans la page
seance11.html
<h1>Les étudiants</h1>
<ul id="liste"></ul>
<p id="stats"></p>app.js
const etudiants = [
{ prenom: "Camille", age: 19, formation: "L2 Info" },
{ prenom: "Alex", age: 21, formation: "L3 Physique" },
{ prenom: "Sam", age: 18, formation: "L1 Sciences" },
];
const liste = document.querySelector("#liste");
const stats = document.querySelector("#stats");
function afficher() {
liste.innerHTML = "";
let totalAge = 0;
for (const etudiant of etudiants) {
const li = document.createElement("li");
li.textContent = `${etudiant.prenom} (${etudiant.age} ans) — ${etudiant.formation}`;
liste.appendChild(li);
totalAge = totalAge + etudiant.age;
}
const moyenne = totalAge / etudiants.length;
stats.textContent = `${etudiants.length} étudiants, âge moyen ${moyenne.toFixed(1)} ans`;
}
afficher();Parce que les informations sont séparées, on peut :
// filtrer
for (const e of etudiants) {
if (e.age >= 20) { … }
}
// trier par âge
etudiants.sort((a, b) => a.age - b.age);
// changer le format d'affichage sans toucher aux données
li.textContent = `${e.formation} : ${e.prenom}`;Avec la version « phrase » de la séance 10, aucun de ces trois exemples n’était possible.
Exercice 11.2
À partir de ce tableau :
const produits = [
{ nom: "Clavier", prix: 25, stock: 4 },
{ nom: "Souris", prix: 12, stock: 0 },
{ nom: "Écran", prix: 150, stock: 2 },
];Affichez dans la page une liste de la forme :
Clavier — 25 € (en stock)
Souris — 12 € (rupture)
Écran — 150 € (en stock)
Puis, en dessous : Valeur totale du stock : X € (prix × stock, additionné).
app.js
const produits = [
{ nom: "Clavier", prix: 25, stock: 4 },
{ nom: "Souris", prix: 12, stock: 0 },
{ nom: "Écran", prix: 150, stock: 2 },
];
const liste = document.querySelector("#liste");
const total = document.querySelector("#total");
let valeur = 0;
liste.innerHTML = "";
for (const produit of produits) {
const li = document.createElement("li");
if (produit.stock > 0) {
li.textContent = `${produit.nom} — ${produit.prix} € (en stock)`;
} else {
li.textContent = `${produit.nom} — ${produit.prix} € (rupture)`;
}
liste.appendChild(li);
valeur = valeur + produit.prix * produit.stock;
}
total.textContent = `Valeur totale du stock : ${valeur} €`;Vérification : 25×4 + 12×0 + 150×2 = 100 + 0 + 300 = 400 €.
4. Des objets qui savent faire des choses
Une propriété peut contenir une fonction. On l’appelle alors une méthode.
const compte = {
titulaire: "Camille",
solde: 100,
afficher() {
return `${this.titulaire} : ${this.solde} €`;
},
};
console.log(compte.afficher()); // Camille : 100 €this = « l’objet dans lequel je suis »
À l’intérieur d’une méthode, this désigne l’objet lui-même.
this.solde veut dire « le solde de ce compte-ci ». Sans this, JavaScript chercherait une variable solde à l’extérieur, et ne la trouverait pas.
Dans une méthode, on écrit toujours this. devant les propriétés de l’objet.
5. La classe : un moule à objets
Écrire dix objets « compte bancaire » à la main serait pénible et source d’erreurs. Une classe est un moule : on la définit une fois, et on fabrique autant d’objets qu’on veut.
class CompteBancaire {
constructor(titulaire, soldeInitial) {
this.titulaire = titulaire;
this.solde = soldeInitial;
}
deposer(montant) {
this.solde = this.solde + montant;
}
retirer(montant) {
if (montant > this.solde) {
return false; // opération refusée
}
this.solde = this.solde - montant;
return true;
}
afficher() {
return `${this.titulaire} : ${this.solde} €`;
}
}On s’en sert avec new :
const compteA = new CompteBancaire("Camille", 100);
const compteB = new CompteBancaire("Alex", 50);
compteA.deposer(20);
console.log(compteA.afficher()); // Camille : 120 €
console.log(compteB.afficher()); // Alex : 50 €| Mot | Sens |
|---|---|
class |
On définit un moule. Aucun objet n’existe encore |
constructor |
La méthode exécutée automatiquement à la création. Elle remplit l’objet |
this |
L’objet en train d’être fabriqué ou utilisé |
new |
« Fabrique-moi un objet avec ce moule » |
Image : la classe est le plan de la maison, new construit une maison réelle. On peut construire dix maisons avec le même plan — elles seront indépendantes.
const c = CompteBancaire("Camille", 100); // ❌ « new » oublié
const c = new CompteBancaire(); // ❌ arguments oubliés → undefined partout
class Compte {
constructor(titulaire) {
titulaire = titulaire; // ❌ « this. » oublié :
} // ne range rien dans l'objet
}L’oubli de this. est le plus vicieux : aucune erreur, mais l’objet reste vide.
Étape 2 — Une classe en action dans la page
banque.html
<h1>Ma banque</h1>
<p id="solde"></p>
<button id="depot">Déposer 10 €</button>
<button id="retrait">Retirer 10 €</button>
<p id="message"></p>banque.js
class CompteBancaire {
constructor(titulaire, soldeInitial) {
this.titulaire = titulaire;
this.solde = soldeInitial;
}
deposer(montant) {
this.solde = this.solde + montant;
}
retirer(montant) {
if (montant > this.solde) {
return false;
}
this.solde = this.solde - montant;
return true;
}
}
const compte = new CompteBancaire("Camille", 50);
const soldeEl = document.querySelector("#solde");
const messageEl = document.querySelector("#message");
function afficher() {
soldeEl.textContent = `${compte.titulaire} : ${compte.solde} €`;
}
document.querySelector("#depot").addEventListener("click", () => {
compte.deposer(10);
messageEl.textContent = "";
afficher();
});
document.querySelector("#retrait").addEventListener("click", () => {
const reussi = compte.retirer(10);
if (!reussi) {
messageEl.textContent = "Solde insuffisant.";
} else {
messageEl.textContent = "";
}
afficher();
});
afficher();solde
Toutes les règles du compte (« on ne peut pas retirer plus que le solde ») sont à l’intérieur de la classe.
Quelqu’un qui utilise CompteBancaire ne peut pas se tromper : il appelle retirer() et la règle s’applique. Si on avait une simple variable let solde, chaque bout de code qui la modifie devrait re-vérifier la règle — et un jour, quelqu’un oublierait.
Une classe range ensemble les données et les règles qui les protègent. C’est ça, la programmation orientée objet.
Vous entendrez souvent parler de TypeScript. C’est du JavaScript auquel on ajoute des étiquettes de type :
class CompteBancaire {
titulaire: string;
solde: number;
constructor(titulaire: string, soldeInitial: number) { … }
deposer(montant: number): void { … }
}L’intérêt : l’éditeur vous prévient avant même d’exécuter si vous écrivez compte.deposer("dix"). Sur un gros projet, ça évite énormément de bugs.
Ce n’est pas au programme de ce cours — mais maintenant, quand vous verrez : string dans du code, vous saurez ce que c’est. L’annexe Astro montre un exemple complet.
6. À toi de jouer
Exercice 11.3 — Prédire
class Chat {
constructor(nom) {
this.nom = nom;
this.faim = 3;
}
manger() {
this.faim = this.faim - 1;
}
}
const a = new Chat("Moustache");
const b = new Chat("Ficelle");
a.manger();
a.manger();
console.log(a.nom, a.faim);
console.log(b.nom, b.faim);Moustache 1
Ficelle 3
Le point essentiel : a et b sont deux objets indépendants. Nourrir a ne change rien à b, même s’ils sortent du même moule.
C’est toute la puissance des classes : un plan, autant d’exemplaires indépendants qu’on veut.
Exercice 11.4 — La classe Etudiant
Écrivez une classe Etudiant avec :
- un
constructor(prenom, notes)oùnotesest un tableau de nombres - une méthode
moyenne()qui renvoie la moyenne - une méthode
estRecu()qui renvoietruesi la moyenne est ≥ 10 - une méthode
bulletin()qui renvoieCamille : moyenne 12.5 — reçu(ou— ajourné)
Testez avec deux étudiants.
app.js
class Etudiant {
constructor(prenom, notes) {
this.prenom = prenom;
this.notes = notes;
}
moyenne() {
let total = 0;
for (const note of this.notes) {
total = total + note;
}
return total / this.notes.length;
}
estRecu() {
return this.moyenne() >= 10;
}
bulletin() {
const resultat = this.estRecu() ? "reçu" : "ajourné";
return `${this.prenom} : moyenne ${this.moyenne().toFixed(1)} — ${resultat}`;
}
}
const a = new Etudiant("Camille", [12, 15, 10]);
const b = new Etudiant("Alex", [8, 7, 9]);
console.log(a.bulletin()); // Camille : moyenne 12.3 — reçu
console.log(b.bulletin()); // Alex : moyenne 8.0 — ajournéTrois points à remarquer :
- Dans la boucle, on écrit
this.notes, pasnotes: le tableau appartient à l’objet. estRecu()réutilisemoyenne(). Une méthode peut en appeler une autre : toujours avecthis..condition ? "a" : "b"est un raccourci deif/elsequi renvoie une valeur. Si ça vous perturbe, écrivez unif/elseclassique — c’est tout aussi correct.
Exercice 11.5 — Le répertoire de contacts 📇
Faites une page contacts.html qui combine tout ce qu’on sait :
- Une classe
Contactavecnom,email,telephoneet une méthoderesume()qui renvoieCamille — camille@exemple.fr - Un formulaire (3 champs) pour ajouter un contact
- Une validation : nom non vide, email contenant un
@ - Le stockage dans un tableau de
Contact - L’affichage de la liste après chaque ajout, avec
createElement - Un compteur :
3 contacts enregistrés
contacts.html
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8" />
<title>Répertoire</title>
<link rel="stylesheet" href="contacts.css" />
</head>
<body>
<h1>Mon répertoire</h1>
<form id="formulaire">
<p>
<label for="nom">Nom :</label>
<input type="text" id="nom" />
</p>
<p>
<label for="email">Email :</label>
<input type="email" id="email" />
</p>
<p>
<label for="tel">Téléphone :</label>
<input type="text" id="tel" />
</p>
<button type="submit">Ajouter</button>
</form>
<p id="erreur" class="erreur"></p>
<ul id="liste"></ul>
<p id="compteur"></p>
<script src="contacts.js"></script>
</body>
</html>contacts.js
class Contact {
constructor(nom, email, telephone) {
this.nom = nom;
this.email = email;
this.telephone = telephone;
}
resume() {
return `${this.nom} — ${this.email}`;
}
}
const formulaire = document.querySelector("#formulaire");
const nomEl = document.querySelector("#nom");
const emailEl = document.querySelector("#email");
const telEl = document.querySelector("#tel");
const erreur = document.querySelector("#erreur");
const liste = document.querySelector("#liste");
const compteur = document.querySelector("#compteur");
const contacts = [];
function afficher() {
liste.innerHTML = "";
for (const contact of contacts) {
const li = document.createElement("li");
li.textContent = contact.resume();
liste.appendChild(li);
}
compteur.textContent = `${contacts.length} contact(s) enregistré(s)`;
}
formulaire.addEventListener("submit", (event) => {
event.preventDefault();
const nom = nomEl.value.trim();
const email = emailEl.value.trim();
const tel = telEl.value.trim();
if (nom === "") {
erreur.textContent = "Le nom est obligatoire.";
return;
}
if (!email.includes("@")) {
erreur.textContent = "L'email doit contenir un @.";
return;
}
erreur.textContent = "";
contacts.push(new Contact(nom, email, tel));
formulaire.reset();
afficher();
});
afficher();L’architecture à retenir, et qui ne changera plus :
classe Contact → décrit UN contact et ce qu'il sait faire
const contacts = [] → les DONNÉES
afficher() → dessine la page à partir des données
écouteur submit → vérifie, modifie les données, redessine
Chaque partie a un rôle unique. C’est exactement comme ça que sont écrites les vraies applications web.
7. Fiche mémo
Un objet
const p = { prenom: "Camille", age: 19 };
p.prenom // lire
p.age = 20; // modifier
p.ville = "Lyon"; // ajouterUn tableau d’objets
const gens = [
{ prenom: "Camille", age: 19 },
{ prenom: "Alex", age: 21 },
];
for (const g of gens) {
console.log(g.prenom);
}Une classe
class Truc {
constructor(a, b) {
this.a = a;
this.b = b;
}
faire() {
return this.a + this.b;
}
}
const t = new Truc(1, 2);
t.faire(); // 3Les 4 pièges
newoubliéthis.oublié dans le constructeur- Majuscule dans le nom d’une propriété →
undefined - Une méthode s’appelle avec parenthèses :
t.faire()