Cours JavaScript et TypeScript · Module 2: Fonctions, tableaux et objets
Propriétés et méthodes des objets
Dans cette leçon9
Dans le système de gestion de Jurassic Park, les propriétés et les méthodes des objets sont essentielles pour modéliser et contrôler le comportement des différents éléments de notre complexe. Dans cette leçon, nous allons examiner de plus près les propriétés et les méthodes des objets JavaScript, en découvrant des techniques avancées qui nous aident à maintenir notre parc et ses habitants en parfait état.
Les descripteurs de propriété
En JavaScript, chaque propriété d'un objet possède non seulement une valeur, mais aussi des attributs qui contrôlent la façon dont cette propriété peut être utilisée. Ces attributs sont ce qu'on appelle les descripteurs de propriété.
1const dinosaur = {
2 name: "Blue"
3};
4
5// Récupération du descripteur de la propriété
6const descriptor = Object.getOwnPropertyDescriptor(dinosaur, "name");
7console.log(descriptor);
8// Résultat : { value: "Blue", writable: true, enumerable: true, configurable: true }Le descripteur de propriété contient les champs suivants :
- value : la valeur actuelle de la propriété
- writable : indique si la propriété peut être modifiée
- enumerable : indique si la propriété apparaît lors d'une itération (par exemple dans une boucle for...in)
- configurable : indique si le descripteur peut être modifié et si la propriété peut être supprimée
Définir des propriétés avec des descripteurs
Nous pouvons définir de nouvelles propriétés avec des descripteurs précis à l'aide de Object.defineProperty() :
1const velociraptor = {};
2
3// On définit la propriété "species" avec des descripteurs précis
4Object.defineProperty(velociraptor, "species", {
5 value: "Velociraptor",
6 writable: false, // On ne peut pas modifier la valeur
7 enumerable: true, // Apparaîtra lors d'une itération
8 configurable: false // On ne peut ni modifier le descripteur ni supprimer la propriété
9});
10
11console.log(velociraptor.species); // "Velociraptor"
12
13// La tentative de modification ne fonctionnera pas (en mode strict, elle lèvera une erreur)
14velociraptor.species = "Tyrannosaurus";
15console.log(velociraptor.species); // Toujours "Velociraptor"
16
17// La tentative de suppression ne fonctionnera pas
18delete velociraptor.species; // Renverra false
19console.log(velociraptor.species); // Toujours "Velociraptor"Nous pouvons aussi définir plusieurs propriétés à la fois :
1const triceratops = {};
2
3Object.defineProperties(triceratops, {
4 species: {
5 value: "Triceratops",
6 writable: false
7 },
8 diet: {
9 value: "herbivore",
10 writable: false
11 },
12 age: {
13 value: 5,
14 writable: true // L'âge peut changer
15 }
16});
17
18console.log(triceratops.species); // "Triceratops"
19console.log(triceratops.diet); // "herbivore"
20console.log(triceratops.age); // 5
21
22triceratops.age = 6;
23console.log(triceratops.age); // 6Les getters et les setters
Les getters et les setters sont des méthodes spéciales qui permettent de contrôler l'accès aux propriétés d'un objet.
1const dinosaur = {
2 _health: 100, // Par convention, le tiret bas indique qu'il s'agit d'une propriété privée
3
4 // Getter - s'exécute quand on essaie de lire la propriété health
5 get health() {
6 console.log("Lecture du niveau de santé");
7 return this._health;
8 },
9
10 // Setter - s'exécute quand on essaie d'affecter une valeur à la propriété health
11 set health(value) {
12 if (value < 0) {
13 console.log("Avertissement : tentative de régler la santé en dessous de 0");
14 this._health = 0;
15 } else if (value > 100) {
16 console.log("Avertissement : tentative de régler la santé au-dessus de 100");
17 this._health = 100;
18 } else {
19 console.log(`Réglage du niveau de santé sur ${value}`);
20 this._health = value;
21 }
22 }
23};
24
25console.log(dinosaur.health); // Déclenche le getter, résultat : 100
26dinosaur.health = 80; // Déclenche le setter
27console.log(dinosaur.health); // 80
28dinosaur.health = 150; // "Avertissement : tentative de régler la santé au-dessus de 100"
29console.log(dinosaur.health); // 100
30dinosaur.health = -20; // "Avertissement : tentative de régler la santé en dessous de 0"
31console.log(dinosaur.health); // 0Nous pouvons aussi définir des getters et des setters à l'aide de Object.defineProperty() :
1const dinosaur = {
2 _name: "Rex",
3 _species: "Tyrannosaurus"
4};
5
6Object.defineProperty(dinosaur, "fullName", {
7 get: function() {
8 return `${this._name} the ${this._species}`;
9 },
10
11 set: function(value) {
12 // On suppose que value a le format "Nom the Espèce"
13 const parts = value.split(" the ");
14 if (parts.length === 2) {
15 this._name = parts[0];
16 this._species = parts[1];
17 } else {
18 console.log("Format invalide. Utilise : 'Nom the Espèce'");
19 }
20 },
21
22 enumerable: true,
23 configurable: true
24});
25
26console.log(dinosaur.fullName); // "Rex the Tyrannosaurus"
27dinosaur.fullName = "Blue the Velociraptor";
28console.log(dinosaur._name); // "Blue"
29console.log(dinosaur._species); // "Velociraptor"Les méthodes d'objet et leurs propriétés avancées
Lier this - la méthode bind
Dans les méthodes, le mot-clé this fait référence à l'objet sur lequel la méthode est appelée. Cependant, dans certains cas (par exemple quand on passe une méthode comme callback), on peut perdre le contexte this. La méthode bind permet de fixer explicitement la valeur de this.
1const rex = {
2 name: "Rex",
3 makeSound: function() {
4 console.log(`${this.name} rugit très fort !`);
5 }
6};
7
8// Fonctionnement normal
9rex.makeSound(); // "Rex rugit très fort !"
10
11// Utilisation problématique - perte du contexte this
12const rexSound = rex.makeSound;
13// rexSound(); // Erreur : this.name vaut undefined
14
15// Solution - utiliser bind pour fixer la valeur de this
16const boundRexSound = rex.makeSound.bind(rex);
17boundRexSound(); // "Rex rugit très fort !"
18
19// On peut aussi lier this à un autre objet
20const blue = { name: "Blue" };
21const blueSound = rex.makeSound.bind(blue);
22blueSound(); // "Blue rugit très fort !"Les méthodes call et apply
Les méthodes call et apply permettent d'appeler une fonction avec une valeur de this et des arguments précis :
1function attack(target, damage) {
2 console.log(`${this.name} attaque ${target} et inflige ${damage} points de dégâts !`);
3}
4
5const trex = { name: "Tyrannosaurus Rex" };
6const raptor = { name: "Velociraptor" };
7
8// On utilise call avec des arguments passés explicitement
9attack.call(trex, "Triceratops", 50);
10// "Tyrannosaurus Rex attaque Triceratops et inflige 50 points de dégâts !"
11
12// On utilise apply avec les arguments dans un tableau
13attack.apply(raptor, ["Gallimimus", 30]);
14// "Velociraptor attaque Gallimimus et inflige 30 points de dégâts !"Les propriétés d'objet sous forme de symboles
Les symboles fournissent des identifiants uniques qui peuvent servir de clés de propriétés d'objet. Ils sont particulièrement utiles pour créer des propriétés "privées" ou pour étendre des objets sans risque de collision de noms.
1// Création de symboles
2const healthSymbol = Symbol('health');
3const nameSymbol = Symbol('name');
4
5const dinosaur = {
6 [nameSymbol]: "Blue",
7 [healthSymbol]: 100,
8 species: "Velociraptor"
9};
10
11console.log(dinosaur[nameSymbol]); // "Blue"
12console.log(dinosaur[healthSymbol]); // 100
13
14// Les symboles ne sont pas visibles dans les boucles for...in
15for (let key in dinosaur) {
16 console.log(key); // Seulement "species", sans nameSymbol ni healthSymbol
17}
18
19// Ni dans Object.keys()
20console.log(Object.keys(dinosaur)); // ["species"]
21
22// Mais on peut les obtenir avec Object.getOwnPropertySymbols()
23console.log(Object.getOwnPropertySymbols(dinosaur)); // [Symbol(name), Symbol(health)]Les symboles bien connus
JavaScript définit plusieurs "symboles bien connus", qui permettent aux classes d'adapter leur comportement. Le plus utilisé est Symbol.iterator, qui permet aux objets d'être itérables.
1// On crée un objet DinosaurPack qui sera itérable
2class DinosaurPack {
3 constructor() {
4 this.dinosaurs = [];
5 }
6
7 addDinosaur(name, species) {
8 this.dinosaurs.push({ name, species });
9 }
10
11 // Implémentation de l'itérabilité
12 [Symbol.iterator]() {
13 let index = 0;
14 const dinosaurs = this.dinosaurs;
15
16 return {
17 next: function() {
18 if (index < dinosaurs.length) {
19 return { value: dinosaurs[index++], done: false };
20 } else {
21 return { done: true };
22 }
23 }
24 };
25 }
26}
27
28const pack = new DinosaurPack();
29pack.addDinosaur("Blue", "Velociraptor");
30pack.addDinosaur("Delta", "Velociraptor");
31pack.addDinosaur("Echo", "Velociraptor");
32pack.addDinosaur("Charlie", "Velociraptor");
33
34// Maintenant, on peut itérer sur notre objet
35for (const dino of pack) {
36 console.log(`${dino.name} est un ${dino.species}`);
37}
38// Résultat :
39// "Blue est un Velociraptor"
40// "Delta est un Velociraptor"
41// "Echo est un Velociraptor"
42// "Charlie est un Velociraptor"Geler, sceller et empêcher l'extension des objets
JavaScript propose trois méthodes pour contrôler dans quelle mesure on peut modifier les objets :
Object.freeze()
Gèle l'objet (de façon superficielle : les objets imbriqués peuvent toujours être modifiés) - on ne peut ni modifier les propriétés existantes ni en ajouter de nouvelles.
1const securityProtocol = {
2 level: "Maximum",
3 status: "Active",
4 lastUpdated: "2023-01-15"
5};
6
7// On gèle l'objet
8Object.freeze(securityProtocol);
9
10// Les tentatives de modification ne fonctionneront pas
11securityProtocol.level = "Minimum"; // Ne changera pas
12securityProtocol.newProperty = "Value"; // Ne sera pas ajoutée
13
14console.log(securityProtocol.level); // Toujours "Maximum"
15console.log(securityProtocol.newProperty); // undefined
16
17console.log(Object.isFrozen(securityProtocol)); // trueObject.seal()
Scelle l'objet - on peut modifier les propriétés existantes, mais on ne peut ni en ajouter de nouvelles ni supprimer celles qui existent.
1const dinoStats = {
2 health: 100,
3 hunger: 50
4};
5
6// On scelle l'objet
7Object.seal(dinoStats);
8
9// On peut modifier les propriétés existantes
10dinoStats.health = 80;
11console.log(dinoStats.health); // 80
12
13// Mais on ne peut pas en ajouter de nouvelles
14dinoStats.happiness = 90; // Ne fonctionnera pas
15console.log(dinoStats.happiness); // undefined
16
17// Ni supprimer celles qui existent
18delete dinoStats.hunger; // Ne fonctionnera pas
19console.log(dinoStats.hunger); // Toujours 50
20
21console.log(Object.isSealed(dinoStats)); // trueObject.preventExtensions()
Empêche l'extension de l'objet - on ne peut pas ajouter de nouvelles propriétés, mais on peut modifier et supprimer celles qui existent.
1const dinosaur = {
2 name: "Blue",
3 species: "Velociraptor"
4};
5
6// On empêche l'extension
7Object.preventExtensions(dinosaur);
8
9// On ne peut pas ajouter de nouvelles propriétés
10dinosaur.age = 4; // Ne fonctionnera pas
11console.log(dinosaur.age); // undefined
12
13// Mais on peut modifier celles qui existent
14dinosaur.name = "Delta";
15console.log(dinosaur.name); // "Delta"
16
17// Et supprimer celles qui existent
18delete dinosaur.species;
19console.log(dinosaur.species); // undefined
20
21console.log(Object.isExtensible(dinosaur)); // falseL'objet Proxy
L'objet Proxy permet d'intercepter et de personnaliser les opérations de base sur les objets.
1const dinosaur = {
2 name: "Blue",
3 species: "Velociraptor",
4 health: 100,
5 hunger: 50,
6 aggressionLevel: 8
7};
8
9// On crée un proxy pour l'objet dinosaur
10const monitoredDinosaur = new Proxy(dinosaur, {
11 // On intercepte l'opération de lecture d'une propriété
12 get: function(target, prop, receiver) {
13 console.log(`Lecture de la propriété : ${prop}`);
14 return Reflect.get(target, prop, receiver);
15 },
16
17 // On intercepte l'opération d'écriture d'une propriété
18 set: function(target, prop, value, receiver) {
19 console.log(`Réglage de ${prop} sur ${value}`);
20
21 // On ajoute une validation pour certaines propriétés
22 if (prop === 'health') {
23 if (value < 0) value = 0;
24 if (value > 100) value = 100;
25
26 // Journalisation de la variation de santé
27 const oldValue = target[prop];
28 const change = value - oldValue;
29 console.log(`Variation de la santé : ${change > 0 ? '+' : ''}${change}%`);
30
31 // On vérifie que le dinosaure n'est pas gravement blessé
32 if (value < 20 && oldValue >= 20) {
33 console.log("ALARME : le dinosaure est gravement blessé !");
34 }
35 }
36
37 if (prop === 'aggressionLevel') {
38 if (value > 7) {
39 console.log("ATTENTION : niveau d'agressivité élevé - il faut renforcer les mesures de sécurité !");
40 }
41 }
42
43 return Reflect.set(target, prop, value, receiver);
44 },
45
46 // On intercepte l'opération de suppression d'une propriété
47 deleteProperty: function(target, prop) {
48 console.log(`Tentative de suppression de ${prop} - opération interdite pour la sécurité des données`);
49 return false; // On n'autorise pas la suppression des propriétés
50 }
51});
52
53// On teste notre proxy
54console.log(monitoredDinosaur.name); // "Lecture de la propriété : name", "Blue"
55monitoredDinosaur.health = 90; // "Réglage de health sur 90", "Variation de la santé : -10%"
56monitoredDinosaur.health = 15; // "Réglage de health sur 15", "Variation de la santé : -75%", "ALARME : le dinosaure est gravement blessé !"
57monitoredDinosaur.aggressionLevel = 9; // "Réglage de aggressionLevel sur 9", "ATTENTION : niveau d'agressivité élevé..."
58delete monitoredDinosaur.hunger; // "Tentative de suppression de hunger - opération interdite pour la sécurité des données"Application pratique : un système de surveillance de la santé des dinosaures
Combinons plusieurs des techniques découvertes pour créer un système avancé de surveillance de la santé des dinosaures de notre parc :
1class DinosaurHealthMonitor {
2 constructor() {
3 this.dinosaurs = new Map();
4 this.alarmThreshold = 30;
5 this.criticalThreshold = 15;
6 this.lastCheckTime = null;
7 }
8
9 // On ajoute un dinosaure au système de surveillance
10 addDinosaur(id, name, species) {
11 if (this.dinosaurs.has(id)) {
12 console.log(`Le dinosaure avec l'ID ${id} existe déjà dans le système.`);
13 return false;
14 }
15
16 const dinosaurData = {
17 id,
18 name,
19 species,
20 _health: 100,
21 _lastFed: new Date(),
22 _status: "Healthy",
23
24 // On utilise un getter et un setter pour la santé
25 get health() {
26 return this._health;
27 },
28
29 set health(value) {
30 const oldHealth = this._health;
31 this._health = Math.max(0, Math.min(100, value)); // Limitation à la plage 0-100
32
33 // Mise à jour du statut en fonction de la santé
34 if (this._health === 0) {
35 this._status = "Critical - Emergency Response Required";
36 } else if (this._health < 15) {
37 this._status = "Critical";
38 } else if (this._health < 30) {
39 this._status = "Poor - Medical Attention Required";
40 } else if (this._health < 60) {
41 this._status = "Fair";
42 } else if (this._health < 90) {
43 this._status = "Good";
44 } else {
45 this._status = "Excellent";
46 }
47
48 return this._health;
49 },
50
51 // Méthode pour nourrir le dinosaure
52 feed() {
53 const now = new Date();
54 const hoursSinceLastFed = (now - this._lastFed) / (1000 * 60 * 60);
55
56 // L'effet du repas dépend du temps écoulé depuis le dernier repas du dinosaure
57 if (hoursSinceLastFed < 1) {
58 console.log(`${this.name} n'a pas faim (nourri il y a moins d'une heure).`);
59 return false;
60 } else {
61 const healthGain = Math.min(10, hoursSinceLastFed * 2); // 10 points de santé au maximum
62 this.health += healthGain;
63 this._lastFed = now;
64
65 console.log(`${this.name} a été nourri. +${healthGain.toFixed(1)} de santé. Niveau actuel : ${this.health}%`);
66 return true;
67 }
68 }
69 };
70
71 // On utilise Object.seal pour empêcher l'ajout de nouvelles propriétés
72 Object.seal(dinosaurData);
73
74 // On ajoute le dinosaure à la Map
75 this.dinosaurs.set(id, dinosaurData);
76 console.log(`Dinosaure ${name} (${species}) ajouté au système de surveillance de la santé.`);
77 return true;
78 }
79
80 // Récupération des informations sur un dinosaure
81 getDinosaurInfo(id) {
82 if (!this.dinosaurs.has(id)) {
83 return `Le dinosaure avec l'ID ${id} n'existe pas dans le système.`;
84 }
85
86 const dino = this.dinosaurs.get(id);
87 return {
88 id: dino.id,
89 name: dino.name,
90 species: dino.species,
91 health: dino.health,
92 status: dino._status,
93 lastFed: dino._lastFed
94 };
95 }
96
97 // Méthode qui simule le passage du temps, ce qui fait baisser la santé des dinosaures
98 simulateTimePass(hours) {
99 console.log(`Simulation du passage de ${hours} heures...`);
100
101 this.dinosaurs.forEach(dino => {
102 // Baisse de la santé - le rythme dépend de l'espèce
103 let healthReduction = hours;
104
105 if (dino.species.toLowerCase().includes('tyrannosaurus')) {
106 healthReduction *= 1.5; // Le Tyrannosaurus perd sa santé plus vite
107 } else if (dino.species.toLowerCase().includes('velociraptor')) {
108 healthReduction *= 1.2; // Les Velociraptors aussi, assez vite
109 }
110
111 const newHealth = dino.health - healthReduction;
112 dino.health = newHealth;
113
114 console.log(`${dino.name} : santé réduite de ${healthReduction.toFixed(1)} à ${dino.health}% (${dino._status})`);
115 });
116
117 this.lastCheckTime = new Date();
118 this.checkAlarms();
119 }
120
121 // Vérification des alarmes de santé
122 checkAlarms() {
123 console.log("Vérification des alarmes de santé...");
124
125 const criticalDinos = [];
126 const alarmDinos = [];
127
128 this.dinosaurs.forEach(dino => {
129 if (dino.health <= this.criticalThreshold) {
130 criticalDinos.push(dino);
131 } else if (dino.health <= this.alarmThreshold) {
132 alarmDinos.push(dino);
133 }
134 });
135
136 if (criticalDinos.length > 0) {
137 console.log("=== ÉTAT DE SANTÉ CRITIQUE ===");
138 criticalDinos.forEach(dino => {
139 console.log(`- ${dino.name} (${dino.species}) : ${dino.health}% - INTERVENTION IMMÉDIATE REQUISE !`);
140 });
141 }
142
143 if (alarmDinos.length > 0) {
144 console.log("=== ALARME SANTÉ ===");
145 alarmDinos.forEach(dino => {
146 console.log(`- ${dino.name} (${dino.species}) : ${dino.health}% - Nécessite des soins vétérinaires`);
147 });
148 }
149
150 if (criticalDinos.length === 0 && alarmDinos.length === 0) {
151 console.log("Tous les dinosaures sont en bonne santé.");
152 }
153 }
154
155 // Génération d'un rapport pour tous les dinosaures
156 generateHealthReport() {
157 const now = new Date();
158 let report = `=== RAPPORT DE SANTÉ DES DINOSAURES (généré : ${now.toLocaleString()}) ===\n`;
159
160 if (this.dinosaurs.size === 0) {
161 report += "Aucun dinosaure dans le système de surveillance.\n";
162 } else {
163 // Tri des dinosaures par niveau de santé (en commençant par le plus bas)
164 const sortedDinos = [...this.dinosaurs.values()].sort((a, b) => a.health - b.health);
165
166 sortedDinos.forEach(dino => {
167 report += `${dino.name} (${dino.species})\n`;
168 report += ` Santé : ${dino.health}% (${dino._status})\n`;
169 report += ` Dernier repas : ${dino._lastFed.toLocaleString()}\n`;
170
171 // On ajoute des recommandations
172 if (dino.health < this.criticalThreshold) {
173 report += " RECOMMANDATION : intervention vétérinaire immédiate ! Transfert en unité de soins intensifs.\n";
174 } else if (dino.health < this.alarmThreshold) {
175 report += " RECOMMANDATION : fréquence accrue des repas et des contrôles vétérinaires.\n";
176 }
177
178 report += "\n";
179 });
180 }
181
182 return report;
183 }
184}
185
186// Exemple d'utilisation
187const healthMonitor = new DinosaurHealthMonitor();
188
189// On ajoute quelques dinosaures
190healthMonitor.addDinosaur("TRX-01", "Rex", "Tyrannosaurus Rex");
191healthMonitor.addDinosaur("VEL-01", "Blue", "Velociraptor");
192healthMonitor.addDinosaur("TRI-01", "Trixie", "Triceratops");
193healthMonitor.addDinosaur("BRACH-01", "Bronty", "Brachiosaurus");
194
195// On génère le rapport initial
196console.log(healthMonitor.generateHealthReport());
197
198// On simule le passage du temps
199healthMonitor.simulateTimePass(24); // 24 heures
200
201// On nourrit certains dinosaures
202const rex = healthMonitor.dinosaurs.get("TRX-01");
203const blue = healthMonitor.dinosaurs.get("VEL-01");
204rex.feed();
205blue.feed();
206
207// On simule un nouveau passage du temps
208healthMonitor.simulateTimePass(48); // 48 heures
209
210// On génère le rapport final
211console.log(healthMonitor.generateHealthReport());Résumé
Dans cette leçon, nous avons découvert les propriétés et les méthodes avancées des objets JavaScript :
- Les descripteurs de propriété - permettent un contrôle précis des attributs des propriétés
- Les getters et les setters - permettent d'exécuter du code lors de la lecture ou de l'écriture d'une propriété
- La liaison de this - les méthodes bind, call et apply aident à contrôler le contexte d'une fonction
- Les symboles - fournissent des identifiants uniques pour les propriétés d'objet
- Le gel des objets - les méthodes freeze, seal et preventExtensions permettent de limiter les modifications des objets
- Proxy - permet d'intercepter et de personnaliser les opérations de base sur les objets
Ces techniques sont indispensables pour créer des systèmes complexes, comme notre système de surveillance de la santé des dinosaures à Jurassic Park. Elles permettent de concevoir des solutions logicielles plus fiables, plus sûres et plus élégantes.
Code de cette leçon : index.js
1// Propriétés et méthodes des objets - Parc Jurassique
2console.log("Propriétés et Méthodes des Objets");
3console.log("Opérations avancées sur les objets\n");
4
5// ===========================================
6// 1. ACCÈS AUX PROPRIÉTÉS
7// ===========================================
8console.log("=== 1. Accès aux propriétés ===");
9
10const dinosaur = {
11 name: "T-Rex",
12 species: "Tyrannosaurus Rex",
13 age: 8,
14 "sector-id": "A-7", // Propriété avec un tiret
15 weight: 8000
16};
17
18// Notation par point
19console.log("\nNotation par point:");
20console.log(` name: ${dinosaur.name}`);
21console.log(` age: ${dinosaur.age}`);
22
23// Notation par crochets
24console.log("\nNotation par crochets:");
25console.log(` ['species']: ${dinosaur['species']}`);
26console.log(` ['sector-id']: ${dinosaur['sector-id']}`); // Obligatoire pour les tirets
27
28// Accès dynamique
29const prop = "weight";
30console.log(`\nAccès dynamique [${prop}]: ${dinosaur[prop]}`);
31
32// ===========================================
33// 2. AJOUT ET MODIFICATION DE PROPRIÉTÉS
34// ===========================================
35console.log("\n=== 2. Ajout et modification ===");
36
37const dino = {
38 name: "Blue",
39 species: "Velociraptor"
40};
41
42console.log("\nInitialement:", dino);
43
44// Ajout de nouvelles propriétés
45dino.age = 5;
46dino.weight = 80;
47dino["sector"] = "B";
48
49console.log("Après ajout:", dino);
50
51// Modification de l'existant
52dino.age = 6;
53dino.weight = 85;
54
55console.log("Après modification:", dino);
56
57// ===========================================
58// 3. SUPPRESSION DE PROPRIÉTÉS
59// ===========================================
60console.log("\n=== 3. Suppression de propriétés ===");
61
62const tempDino = {
63 name: "Charlie",
64 species: "Velociraptor",
65 tempId: 12345,
66 debugMode: true
67};
68
69console.log("\nAvant suppression:", tempDino);
70
71delete tempDino.tempId;
72delete tempDino.debugMode;
73
74console.log("Après suppression:", tempDino);
75
76// ===========================================
77// 4. VÉRIFICATION DE L'EXISTENCE DE PROPRIÉTÉS
78// ===========================================
79console.log("\n=== 4. Vérification de l'existence ===");
80
81const checkDino = {
82 name: "Rexy",
83 age: 8,
84 sector: null
85};
86
87// Opérateur 'in'
88console.log("\nOpérateur 'in':");
89console.log(` 'name' in checkDino: ${'name' in checkDino}`);
90console.log(` 'weight' in checkDino: ${'weight' in checkDino}`);
91console.log(` 'sector' in checkDino: ${'sector' in checkDino}`); // true malgré null !
92
93// hasOwnProperty()
94console.log("\nhasOwnProperty():");
95console.log(` hasOwnProperty('name'): ${checkDino.hasOwnProperty('name')}`);
96console.log(` hasOwnProperty('toString'): ${checkDino.hasOwnProperty('toString')}`); // false (héritée)
97
98// Vérification avec undefined
99console.log("\nVérification undefined:");
100console.log(` name !== undefined: ${checkDino.name !== undefined}`);
101console.log(` weight !== undefined: ${checkDino.weight !== undefined}`);
102
103// ===========================================
104// 5. ITÉRATION SUR LES PROPRIÉTÉS
105// ===========================================
106console.log("\n=== 5. Itération sur les propriétés ===");
107
108const iterDino = {
109 name: "Trike",
110 species: "Triceratops",
111 age: 7,
112 weight: 9000,
113 sector: "C"
114};
115
116// for...in
117console.log("\nfor...in:");
118for (const key in iterDino) {
119 console.log(` ${key}: ${iterDino[key]}`);
120}
121
122// Object.keys()
123console.log("\nObject.keys():");
124const keys = Object.keys(iterDino);
125console.log(` Clés: ${keys.join(", ")}`);
126
127// Object.values()
128console.log("\nObject.values():");
129const values = Object.values(iterDino);
130console.log(` Valeurs: ${values.join(", ")}`);
131
132// Object.entries()
133console.log("\nObject.entries():");
134Object.entries(iterDino).forEach(([key, value]) => {
135 console.log(` ${key} => ${value}`);
136});
137
138// ===========================================
139// 6. MÉTHODES DES OBJETS
140// ===========================================
141console.log("\n=== 6. Méthodes des objets ===");
142
143const methodDino = {
144 name: "Stego",
145 species: "Stegosaurus",
146 health: 85,
147
148 // Méthode traditionnelle
149 getInfo: function() {
150 return `${this.name} (${this.species})`;
151 },
152
153 // Méthode abrégée (ES6)
154 roar() {
155 return `${this.name} rugit: ROAARRR!`;
156 },
157
158 // Méthode avec des paramètres
159 updateHealth(change) {
160 this.health += change;
161 if (this.health > 100) this.health = 100;
162 if (this.health < 0) this.health = 0;
163 return `Santé: ${this.health}%`;
164 },
165
166 // Méthode qui retourne un objet
167 getStats() {
168 return {
169 name: this.name,
170 health: this.health,
171 status: this.health >= 80 ? "En bonne santé" : "Nécessite de l'attention"
172 };
173 }
174};
175
176console.log("\n", methodDino.getInfo());
177console.log(" ", methodDino.roar());
178console.log(" ", methodDino.updateHealth(10));
179console.log(" Stats:", methodDino.getStats());
180
181// ===========================================
182// 7. THIS DANS LES MÉTHODES
183// ===========================================
184console.log("\n=== 7. Contexte de 'this' dans les méthodes ===");
185
186const thisDino = {
187 name: "Brachio",
188 sector: "D",
189
190 showInfo() {
191 console.log(`\n${this.name} dans le secteur ${this.sector}`);
192 },
193
194 // Problème avec une fonction imbriquée
195 showWithTraditional() {
196 console.log(`\n${this.name}:`);
197
198 ["Nourrissage", "Examen", "Nettoyage"].forEach(function(task) {
199 // this ici NE fait PAS référence à thisDino !
200 console.log(` - ${task}`); // On ne peut pas utiliser this.name
201 });
202 },
203
204 // Solution avec une fonction fléchée
205 showWithArrow() {
206 console.log(`\n${this.name}:`);
207
208 ["Nourrissage", "Examen", "Nettoyage"].forEach(task => {
209 console.log(` - ${task} pour ${this.name}`); // this fonctionne !
210 });
211 }
212};
213
214thisDino.showInfo();
215thisDino.showWithTraditional();
216thisDino.showWithArrow();
217
218// ===========================================
219// 8. NOMS DE PROPRIÉTÉS CALCULÉS
220// ===========================================
221console.log("\n=== 8. Noms de propriétés calculés ===");
222
223const propName = "species";
224const propValue = "Velociraptor";
225const sectorNumber = 7;
226
227const computedDino = {
228 name: "Echo",
229 [propName]: propValue, // Nom de propriété calculé
230 [`sector-${sectorNumber}`]: true,
231 [`health_${Date.now()}`]: 95
232};
233
234console.log("\nObjet avec des noms calculés:");
235console.log(computedDino);
236
237// ===========================================
238// 9. DESCRIPTEURS DE PROPRIÉTÉS
239// ===========================================
240console.log("\n=== 9. Descripteurs de propriétés ===");
241
242const descriptorDino = {};
243
244// Définition de propriétés avec des descripteurs
245Object.defineProperty(descriptorDino, 'name', {
246 value: "Rex",
247 writable: true,
248 enumerable: true,
249 configurable: true
250});
251
252Object.defineProperty(descriptorDino, 'id', {
253 value: 12345,
254 writable: false, // Lecture seule
255 enumerable: false, // N'apparaît pas dans for...in
256 configurable: false // Impossible de supprimer ou de modifier le descripteur
257});
258
259console.log("\nPropriétés:");
260console.log(` name: ${descriptorDino.name}`);
261console.log(` id: ${descriptorDino.id}`);
262
263console.log("\nTentative de modification:");
264descriptorDino.name = "Rexy"; // Fonctionne
265console.log(` name après changement: ${descriptorDino.name}`);
266
267descriptorDino.id = 99999; // Ne fonctionne pas (writable: false)
268console.log(` id après tentative de changement: ${descriptorDino.id}`);
269
270console.log("\nfor...in (id n'apparaît pas):");
271for (const key in descriptorDino) {
272 console.log(` ${key}: ${descriptorDino[key]}`);
273}
274
275// ===========================================
276// 10. EXEMPLE COMPLET
277// ===========================================
278console.log("\n=== 10. Système de gestion d'un dinosaure - exemple complet ===");
279
280class DinosaurManager {
281 constructor(name, species) {
282 // Propriétés de base
283 this._name = name;
284 this._species = species;
285 this._health = 100;
286 this._fed = false;
287 this._lastFed = null;
288 this._activities = [];
289
290 // Propriétés calculées
291 Object.defineProperty(this, 'id', {
292 value: `DINO-${Date.now()}`,
293 writable: false,
294 enumerable: true
295 });
296 }
297
298 // Getters et setters
299 get name() {
300 return this._name;
301 }
302
303 get species() {
304 return this._species;
305 }
306
307 get health() {
308 return this._health;
309 }
310
311 set health(value) {
312 if (value < 0) value = 0;
313 if (value > 100) value = 100;
314
315 const oldHealth = this._health;
316 this._health = value;
317
318 this.logActivity(`Santé changée de ${oldHealth}% à ${value}%`);
319 }
320
321 get status() {
322 if (this._health >= 80) return "Excellent";
323 if (this._health >= 50) return "Bon";
324 if (this._health >= 20) return "Faible";
325 return "Critique";
326 }
327
328 get needsFeeding() {
329 if (!this._lastFed) return true;
330 const hoursSinceLastFed = (Date.now() - this._lastFed) / (1000 * 60 * 60);
331 return hoursSinceLastFed > 8;
332 }
333
334 // Méthodes d'action
335 feed() {
336 this._fed = true;
337 this._lastFed = Date.now();
338 this.health += 5;
339 this.logActivity("Nourri");
340 return `${this._name} a été nourri`;
341 }
342
343 heal(amount) {
344 this.health += amount;
345 this.logActivity(`Soigné (+${amount}% de santé)`);
346 return `${this._name} soigné de ${amount}%`;
347 }
348
349 exercise() {
350 this.health -= 5;
351 this.logActivity("Exercice");
352 return `${this._name} a fait de l'exercice`;
353 }
354
355 // Méthodes auxiliaires
356 logActivity(activity) {
357 this._activities.push({
358 activity,
359 timestamp: new Date().toISOString(),
360 health: this._health
361 });
362 }
363
364 getActivityLog(limit = 5) {
365 return this._activities.slice(-limit);
366 }
367
368 getInfo() {
369 return {
370 id: this.id,
371 name: this._name,
372 species: this._species,
373 health: this._health,
374 status: this.status,
375 fed: this._fed,
376 needsFeeding: this.needsFeeding,
377 activitiesCount: this._activities.length
378 };
379 }
380
381 displayReport() {
382 console.log(`\n==================================================`);
383 console.log(`RAPPORT DU DINOSAURE: ${this._name}`);
384 console.log("=".repeat(50));
385
386 const info = this.getInfo();
387 console.log(`ID: ${info.id}`);
388 console.log(`Espèce: ${info.species}`);
389 console.log(`Santé: ${info.health}% (${info.status})`);
390 console.log(`Nourri: ${info.fed ? "Oui" : "Non"}`);
391 console.log(`Nécessite nourriture: ${info.needsFeeding ? "Oui" : "Non"}`);
392 console.log(`Nombre d'activités: ${info.activitiesCount}`);
393
394 const recentActivities = this.getActivityLog(5);
395 if (recentActivities.length > 0) {
396 console.log(`\nDernières activités:`);
397 recentActivities.forEach((act, i) => {
398 const time = new Date(act.timestamp).toLocaleTimeString();
399 console.log(` ${i + 1}. [${time}] ${act.activity} (santé: ${act.health}%)`);
400 });
401 }
402 }
403
404 // Méthode statique
405 static compare(dino1, dino2) {
406 console.log(`\nComparaison: ${dino1.name} vs ${dino2.name}`);
407 console.log(` Santé: ${dino1.health}% vs ${dino2.health}%`);
408 console.log(` Statut: ${dino1.status} vs ${dino2.status}`);
409
410 if (dino1.health > dino2.health) {
411 console.log(` ${dino1.name} est en meilleure santé`);
412 } else if (dino2.health > dino1.health) {
413 console.log(` ${dino2.name} est en meilleure santé`);
414 } else {
415 console.log(` Les deux ont la même santé`);
416 }
417 }
418}
419
420// Démonstration du système
421const rexy = new DinosaurManager("Rexy", "T-Rex");
422const blue = new DinosaurManager("Blue", "Velociraptor");
423
424console.log();
425console.log(rexy.feed());
426console.log(rexy.exercise());
427console.log(rexy.heal(10));
428
429console.log();
430console.log(blue.feed());
431console.log(blue.exercise());
432console.log(blue.exercise());
433
434rexy.displayReport();
435blue.displayReport();
436
437DinosaurManager.compare(rexy, blue);
438
439console.log("\nFélicitations ! Tu maîtrises maintenant les propriétés et les méthodes des objets !");Tu as repéré une erreur dans cette leçon ?
Teste tes connaissances
Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.
1. À quoi sert Object.defineProperty() en JavaScript ?
2. À quoi servent les getters (get) et les setters (set) dans les objets JavaScript ?
Tâches pratiques dans le jeu
- Clique dans l'ordre
Range les éléments de la définition d'un getter dans un objet :