Cours JavaScript et TypeScript · Module 2: Fonctions, tableaux et objets
Le mot-clé this en JavaScript
Dans cette leçon9
Bienvenue de retour à Jurassic Park ! Aujourd'hui, nous allons plonger en profondeur dans l'un des sujets les plus intrigants et parfois déroutants de JavaScript : le mot-clé this. Comprendre this est crucial pour une programmation orientée objet efficace en JavaScript et pour construire des systèmes complexes comme notre système de gestion du parc.
Qu'est-ce que this ?
Le mot-clé this en JavaScript désigne l'objet dans le contexte duquel une fonction est exécutée. Tu peux le voir comme le « lieu de travail actuel » - tout comme un employé de Jurassic Park peut travailler à différents endroits (le labo, l'enclos du T-Rex, le centre de contrôle), une fonction peut être exécutée dans différents contextes.
La valeur de this n'est pas déterminée au moment où la fonction est définie, mais au moment où elle est appelée, ce qui peut créer une certaine confusion. La valeur de this dépend de la façon dont la fonction est invoquée :
- Dans le contexte global
- Comme méthode d'objet
- Comme constructeur
- Avec les méthodes
call(),apply()oubind() - Dans les fonctions fléchées
Examinons chacun de ces cas.
1. this dans le contexte global
Quand this est utilisé dans une fonction classique (qui n'est pas appelée comme méthode d'objet, hors mode strict), il désigne l'objet global : window dans le navigateur et global dans Node.js (globalThis est le nom commun aux deux). Au niveau supérieur d'un script dans le navigateur, this vaut aussi window, mais dans un fichier Node.js c'est module.exports, c'est-à-dire un objet vide {}, et dans les modules ES c'est undefined.
1// Dans la portée globale
2console.log(this); // window dans le navigateur, {} (module.exports) dans un fichier Node.js
3
4// Dans une fonction classique
5function checkGlobalThis() {
6 console.log(this); // aussi l'objet window/global
7}
8
9checkGlobalThis();En mode strict ("use strict";), this dans une fonction classique vaudra undefined au lieu de l'objet global, ce qui aide à éviter la création accidentelle de variables globales.
1"use strict";
2
3function strictThis() {
4 console.log(this); // undefined
5}
6
7strictThis();2. this comme méthode d'objet
Quand une fonction est appelée comme méthode d'un objet, this désigne cet objet. C'est l'un des usages les plus courants et les plus intuitifs de this en JavaScript.
1// Objet représentant un dinosaure
2const velociraptor = {
3 name: "Blue",
4 species: "Velociraptor",
5 age: 5,
6
7 // Méthode utilisant this
8 describe: function() {
9 console.log(`Ce ${this.species} s'appelle ${this.name} et a ${this.age} ans.`);
10 },
11
12 // Méthode qui modifie les données du dinosaure
13 ageDinosaur: function(years) {
14 this.age += years;
15 console.log(`${this.name} a maintenant ${this.age} ans.`);
16 }
17};
18
19// Appel des méthodes
20velociraptor.describe(); // "Ce Velociraptor s'appelle Blue et a 5 ans."
21velociraptor.ageDinosaur(2); // "Blue a maintenant 7 ans."Dans l'exemple ci-dessus, this à l'intérieur des méthodes describe et ageDinosaur désigne l'objet velociraptor, ce qui permet aux méthodes d'accéder à ses propriétés.
Piège potentiel : la perte de contexte
Attention toutefois - le contexte this peut être perdu si tu assignes une méthode à une autre variable et que tu l'appelles comme une fonction classique :
1const velociraptor = {
2 name: "Blue",
3 describe: function() {
4 console.log(`Salut, je suis ${this.name} !`);
5 }
6};
7
8// Appel comme méthode - this désigne velociraptor
9velociraptor.describe(); // "Salut, je suis Blue !"
10
11// Mais quand on assigne la méthode à une variable...
12const describeDino = velociraptor.describe;
13
14// ...et qu'on l'appelle comme une fonction classique
15describeDino(); // "Salut, je suis undefined !"
16// (ou "Salut, je suis !" s'il existe une variable globale name)C'est un problème courant lorsqu'on passe des méthodes comme callbacks :
1const trex = {
2 name: "Rex",
3 roar: function() {
4 console.log(`${this.name} rugit fort !`);
5 }
6};
7
8// L'appel direct fonctionne
9trex.roar(); // "Rex rugit fort !"
10
11// Mais comme callback après 1 seconde...
12setTimeout(trex.roar, 1000); // this ne pointe pas vers trex, donc le nom Rex n'apparaîtra pas
13
14// Une solution (parmi d'autres) est d'utiliser une fonction fléchée ou bind() :
15setTimeout(() => trex.roar(), 1000); // "Rex rugit fort !"
16setTimeout(trex.roar.bind(trex), 1000); // "Rex rugit fort !"3. this dans les constructeurs
Quand une fonction est appelée avec l'opérateur new (comme constructeur), this désigne l'objet nouvellement créé. C'est un mécanisme fondamental pour créer des instances d'objets en JavaScript.
1// Fonction constructeur pour les dinosaures
2function Dinosaur(name, species, age) {
3 // 'this' désigne l'objet nouvellement créé
4 this.name = name;
5 this.species = species;
6 this.age = age;
7
8 this.describe = function() {
9 console.log(`${this.name} est un ${this.species} âgé de ${this.age} ans.`);
10 };
11}
12
13// Création de nouvelles instances
14const blue = new Dinosaur("Blue", "Velociraptor", 5);
15const rex = new Dinosaur("Rex", "Tyrannosaurus", 8);
16
17blue.describe(); // "Blue est un Velociraptor âgé de 5 ans."
18rex.describe(); // "Rex est un Tyrannosaurus âgé de 8 ans."
19
20// Modifions l'âge de Blue
21blue.age = 6;
22blue.describe(); // "Blue est un Velociraptor âgé de 6 ans."Dans le JavaScript moderne (ES6+), au lieu des fonctions constructeur, on utilise souvent des classes, qui offrent une syntaxe plus propre pour faire la même chose :
1// Classe ES6 pour les dinosaures
2class Dinosaur {
3 constructor(name, species, age) {
4 this.name = name;
5 this.species = species;
6 this.age = age;
7 }
8
9 // Les méthodes sont automatiquement ajoutées au prototype
10 describe() {
11 console.log(`${this.name} est un ${this.species} âgé de ${this.age} ans.`);
12 }
13
14 // Méthode qui change l'âge
15 growOlder(years = 1) {
16 this.age += years;
17 console.log(`${this.name} a maintenant ${this.age} ans.`);
18 }
19}
20
21// Création d'instances
22const trixie = new Dinosaur("Trixie", "Triceratops", 7);
23trixie.describe(); // "Trixie est un Triceratops âgé de 7 ans."
24trixie.growOlder(3); // "Trixie a maintenant 10 ans."4. Contrôler this : call(), apply() et bind()
JavaScript offre trois méthodes qui permettent de contrôler explicitement la valeur de this lors de l'appel d'une fonction :
call()
La méthode call() appelle une fonction avec une valeur this précise et des arguments passés individuellement :
1function examineSpecimen(testType, labNumber) {
2 console.log(`Examen du ${this.species} nommé ${this.name}...`);
3 console.log(`Type de test : ${testType}, Laboratoire : ${labNumber}`);
4}
5
6const specimen = {
7 name: "Echo",
8 species: "Velociraptor",
9 age: 3
10};
11
12// Appel de la fonction avec 'this' défini sur specimen
13examineSpecimen.call(specimen, "ADN", "Lab-A");
14// "Examen du Velociraptor nommé Echo..."
15// "Type de test : ADN, Laboratoire : Lab-A"apply()
La méthode apply() fonctionne comme call(), mais les arguments sont passés sous forme de tableau :
1// Même fonction
2function examineSpecimen(testType, labNumber) {
3 console.log(`Examen du ${this.species} nommé ${this.name}...`);
4 console.log(`Type de test : ${testType}, Laboratoire : ${labNumber}`);
5}
6
7// Appel avec les arguments dans un tableau
8examineSpecimen.apply(specimen, ["Comportemental", "Lab-B"]);
9// "Examen du Velociraptor nommé Echo..."
10// "Type de test : Comportemental, Laboratoire : Lab-B"bind()
La méthode bind() crée une nouvelle fonction avec une valeur this assignée de façon permanente :
1const dinosaur = {
2 name: "Delta",
3 species: "Velociraptor"
4};
5
6function makeSound(sound) {
7 console.log(`${this.name} émet un son : ${sound} !`);
8}
9
10// Création d'une nouvelle fonction avec 'this' lié à dinosaur
11const dinosaurSound = makeSound.bind(dinosaur);
12
13// On peut maintenant appeler cette fonction plusieurs fois
14dinosaurSound("Grognement"); // "Delta émet un son : Grognement !"
15dinosaurSound("Sifflement"); // "Delta émet un son : Sifflement !"
16
17// On peut aussi pré-lier des arguments
18const deltaRoar = makeSound.bind(dinosaur, "ROAR");
19deltaRoar(); // "Delta émet un son : ROAR !"Exemple pratique : système de suivi des dinosaures
L'exemple suivant montre comment différentes méthodes de contrôle de this peuvent servir à créer un système de surveillance flexible :
1const dinoMonitoringSystem = {
2 activeDinosaurs: [],
3
4 // Enregistrer un nouveau dinosaure
5 registerDinosaur: function(dinosaur) {
6 this.activeDinosaurs.push(dinosaur);
7 console.log(`Nouveau dinosaure enregistré : ${dinosaur.name} (${dinosaur.species})`);
8 },
9
10 // Trouver un dinosaure par son nom
11 findDinosaurByName: function(name) {
12 return this.activeDinosaurs.find(dino => dino.name === name);
13 },
14
15 // Déclencher une procédure d'alerte
16 triggerAlert: function(message) {
17 console.log(`ALERTE SYSTÈME : ${message}`);
18
19 // Notifier au sujet de tous les dinosaures actifs
20 this.activeDinosaurs.forEach(function(dino) {
21 console.log(`- ${dino.name} (${dino.species}) dans le secteur ${dino.sector}`);
22 });
23 }
24};
25
26// Classe Dinosaur avec une méthode de déplacement
27class Dinosaur {
28 constructor(name, species, sector) {
29 this.name = name;
30 this.species = species;
31 this.sector = sector;
32 this.status = "calme";
33 }
34
35 move(newSector, monitoringSystem) {
36 // On peut passer monitoringSystem en paramètre
37 console.log(`${this.name} se déplace du secteur ${this.sector} au secteur ${newSector}`);
38 this.sector = newSector;
39
40 // Ou utiliser call() pour invoquer une méthode avec le bon contexte
41 const alertMessage = `Le dinosaure ${this.name} est entré dans le secteur ${newSector} !`;
42 monitoringSystem.triggerAlert.call(monitoringSystem, alertMessage);
43 }
44
45 // Méthode qui change le statut du dinosaure
46 updateStatus(newStatus) {
47 this.status = newStatus;
48 console.log(`Statut du dinosaure ${this.name} changé en : ${this.status}`);
49 return this;
50 }
51}
52
53// Initialiser le système et les dinosaures
54const rex = new Dinosaur("Rex", "Tyrannosaurus", "A");
55const blue = new Dinosaur("Blue", "Velociraptor", "B");
56const delta = new Dinosaur("Delta", "Velociraptor", "B");
57
58// Enregistrer les dinosaures dans le système
59dinoMonitoringSystem.registerDinosaur(rex);
60dinoMonitoringSystem.registerDinosaur(blue);
61dinoMonitoringSystem.registerDinosaur(delta);
62
63// Déplacer un dinosaure
64blue.move("C", dinoMonitoringSystem);
65
66// Chaînage de méthodes (grâce à return this)
67rex.updateStatus("agité").updateStatus("agressif");
68
69// Utilisation de bind() pour créer une fonction avec un 'this' prédéfini
70const checkRexStatus = function() {
71 console.log(`Statut du T-Rex : ${this.status}`);
72};
73
74const boundCheckStatus = checkRexStatus.bind(rex);
75boundCheckStatus(); // "Statut du T-Rex : agressif"5. this dans les fonctions fléchées
Les fonctions fléchées, introduites en ES6, gèrent this différemment des fonctions classiques. Elles n'ont pas leur propre this ; à la place, elles « héritent » de this de la portée lexicale environnante.
1const parkSystem = {
2 name: "Jurassic Park",
3 dinosaurs: ["T-Rex", "Velociraptor", "Triceratops"],
4
5 // Méthode utilisant une fonction fléchée
6 listDinosaurs: function() {
7 console.log(`${this.name} contient les dinosaures suivants :`);
8
9 // La fonction fléchée préserve 'this' de la portée de listDinosaurs
10 this.dinosaurs.forEach(dino => {
11 console.log(`- ${dino} dans le parc ${this.name}`);
12 });
13
14 // Pour comparaison - une fonction classique perdrait le contexte 'this'
15 /*
16 this.dinosaurs.forEach(function(dino) {
17 // 'this' désigne l'objet global ou vaut undefined en mode strict
18 console.log(`- ${dino} dans le parc ${this.name}`); // this.name serait undefined
19 });
20 */
21 }
22};
23
24parkSystem.listDinosaurs();
25// "Jurassic Park contient les dinosaures suivants :"
26// "- T-Rex dans le parc Jurassic Park"
27// "- Velociraptor dans le parc Jurassic Park"
28// "- Triceratops dans le parc Jurassic Park"Les fonctions fléchées sont particulièrement utiles quand tu veux préserver le contexte this dans des fonctions imbriquées, des callbacks ou de la programmation asynchrone.
Exemple pratique : système de surveillance avec minuteries
1class DinosaurMonitor {
2 constructor(name, species) {
3 this.name = name;
4 this.species = species;
5 this.status = "Normal";
6 this.lastUpdated = new Date();
7 this.checkInterval = null;
8 }
9
10 // Démarrer la surveillance avec des vérifications de statut régulières
11 startMonitoring() {
12 console.log(`Démarrage de la surveillance du dinosaure : ${this.name}`);
13
14 // La fonction fléchée préserve le contexte 'this'
15 this.checkInterval = setInterval(() => {
16 this.checkStatus();
17 }, 5000);
18
19 // Si on utilisait une fonction classique, on perdrait le contexte :
20 /*
21 this.checkInterval = setInterval(function() {
22 // ici 'this' désigne l'objet global, pas l'instance de DinosaurMonitor
23 this.checkStatus(); // Erreur : this.checkStatus is not a function
24 }, 5000);
25 */
26 }
27
28 // Vérifier le statut du dinosaure
29 checkStatus() {
30 const now = new Date();
31 const timeSinceLastUpdate = (now - this.lastUpdated) / 1000; // en secondes
32
33 console.log(`Vérification du statut du dinosaure ${this.name} (${this.species})...`);
34 console.log(`Dernière mise à jour : il y a ${timeSinceLastUpdate.toFixed(1)} secondes`);
35 console.log(`Statut : ${this.status}`);
36
37 this.lastUpdated = now;
38
39 // Simulation d'un changement de statut (dans un vrai système, on aurait ici les données des capteurs)
40 if (Math.random() < 0.3) {
41 const possibleStatuses = ["Normal", "Agité", "Au repos", "Se nourrit", "Agressif"];
42 this.status = possibleStatuses[Math.floor(Math.random() * possibleStatuses.length)];
43 console.log(`Nouveau statut : ${this.status}`);
44 }
45 }
46
47 // Arrêter la surveillance
48 stopMonitoring() {
49 if (this.checkInterval) {
50 clearInterval(this.checkInterval);
51 this.checkInterval = null;
52 console.log(`Surveillance du dinosaure arrêtée : ${this.name}`);
53 }
54 }
55}
56
57// Créer et démarrer un moniteur
58const blueMonitor = new DinosaurMonitor("Blue", "Velociraptor");
59blueMonitor.startMonitoring();
60
61// Arrêter le moniteur après 20 secondes
62setTimeout(() => {
63 blueMonitor.stopMonitoring();
64}, 20000);Les pièges courants avec this
Le mot-clé this peut être source de nombreux bugs en JavaScript. Voici quelques pièges courants et comment les éviter :
1. Perdre le contexte dans les fonctions imbriquées
1const dinosaur = {
2 name: "Rex",
3 behaviors: ["chasser", "se reposer", "courir"],
4
5 logBehaviors: function() {
6 // ici 'this' désigne 'dinosaur'
7 console.log(`Le dinosaure ${this.name} présente les comportements suivants :`);
8
9 // Problème : 'this' dans la fonction forEach désigne l'objet global
10 this.behaviors.forEach(function(behavior) {
11 // 'this.name' est undefined
12 console.log(`- ${this.name} aime ${behavior}`);
13 });
14 }
15};
16
17dinosaur.logBehaviors();
18// "Le dinosaure Rex présente les comportements suivants :"
19// "- undefined aime chasser"
20// "- undefined aime se reposer"
21// "- undefined aime courir"Solutions :
- Utiliser une fonction fléchée :
1const dinosaur = {
2 name: "Rex",
3 behaviors: ["chasser", "se reposer", "courir"],
4
5 logBehaviors: function() {
6 console.log(`Le dinosaure ${this.name} présente les comportements suivants :`);
7
8 // La fonction fléchée préserve 'this' de la portée environnante
9 this.behaviors.forEach(behavior => {
10 console.log(`- ${this.name} aime ${behavior}`);
11 });
12 }
13};- Stocker une référence à
thisdans une variable :
1const dinosaur = {
2 name: "Rex",
3 behaviors: ["chasser", "se reposer", "courir"],
4
5 logBehaviors: function() {
6 console.log(`Le dinosaure ${this.name} présente les comportements suivants :`);
7
8 // Stocke la référence à 'this' dans une variable 'self' ou 'that'
9 const self = this;
10 this.behaviors.forEach(function(behavior) {
11 console.log(`- ${self.name} aime ${behavior}`);
12 });
13 }
14};- Utiliser la méthode
bind():
1const dinosaur = {
2 name: "Rex",
3 behaviors: ["chasser", "se reposer", "courir"],
4
5 logBehaviors: function() {
6 console.log(`Le dinosaure ${this.name} présente les comportements suivants :`);
7
8 // Utilisation de bind() pour attacher 'this'
9 this.behaviors.forEach(function(behavior) {
10 console.log(`- ${this.name} aime ${behavior}`);
11 }.bind(this));
12 }
13};2. Les méthodes comme callbacks
Quand une méthode d'objet est passée comme callback, elle perd son contexte this :
1class DinosaurTracker {
2 constructor(name) {
3 this.name = name;
4 this.tracked = false;
5 }
6
7 startTracking() {
8 this.tracked = true;
9 console.log(`Suivi du dinosaure ${this.name} démarré`);
10 }
11}
12
13const blueTracker = new DinosaurTracker("Blue");
14
15// Fonctionne correctement
16blueTracker.startTracking();
17
18// Ne fonctionne pas - 'this' ne désigne pas blueTracker
19setTimeout(blueTracker.startTracking, 1000); // this ne pointe pas vers blueTrackerSolutions :
- Utiliser une fonction fléchée :
1setTimeout(() => blueTracker.startTracking(), 1000);- Utiliser
bind():
1setTimeout(blueTracker.startTracking.bind(blueTracker), 1000);Exemple avancé : système de gestion du parc
Voici un exemple plus complexe d'un système de gestion de Jurassic Park qui utilise différents aspects du mot-clé this :
1// Système principal de gestion du parc
2const parkManagementSystem = {
3 parkName: "Jurassic Park",
4 status: "opérationnel",
5 securityLevel: "normal",
6
7 // Sous-systèmes du parc
8 subsystems: {
9 power: { status: "en ligne", output: "100%" },
10 security: { status: "active", fences: "opérationnelles" },
11 visitorCenter: { status: "ouvert", visitors: 120 }
12 },
13
14 // Méthode d'initialisation du système
15 initialize: function() {
16 console.log(`Initialisation du système de gestion du parc : ${this.parkName}`);
17
18 // Vérifier tous les sous-systèmes
19 this.checkAllSubsystems();
20
21 // Mettre en place la surveillance régulière
22 this.startMonitoring();
23
24 return this;
25 },
26
27 // Vérifier le statut des sous-systèmes
28 checkAllSubsystems: function() {
29 console.log("Vérification du statut de tous les sous-systèmes...");
30
31 // Utilisation de Object.entries et d'une fonction fléchée pour préserver le contexte 'this'
32 Object.entries(this.subsystems).forEach(([name, system]) => {
33 console.log(`- Sous-système ${name} : ${system.status}`);
34
35 // Appeler les vérifications spécifiques de chaque sous-système
36 this[`check${name.charAt(0).toUpperCase() + name.slice(1)}`]();
37 });
38 },
39
40 // Méthodes de vérification spécifiques à chaque sous-système
41 checkPower: function() {
42 const powerSystem = this.subsystems.power;
43 console.log(` Vérification de l'alimentation : ${powerSystem.status}, Sortie : ${powerSystem.output}`);
44 },
45
46 checkSecurity: function() {
47 const securitySystem = this.subsystems.security;
48 console.log(` Vérification de la sécurité : ${securitySystem.status}, Clôtures : ${securitySystem.fences}`);
49 },
50
51 checkVisitorCenter: function() {
52 const visitorSystem = this.subsystems.visitorCenter;
53 console.log(` Vérification du centre des visiteurs : ${visitorSystem.status}, Visiteurs : ${visitorSystem.visitors}`);
54 },
55
56 // Démarrer la surveillance régulière
57 startMonitoring: function() {
58 console.log("Démarrage de la surveillance régulière du parc...");
59
60 // Fonction fléchée pour préserver le contexte 'this'
61 this.monitoringInterval = setInterval(() => {
62 this.checkSecurityStatus();
63 }, 10000);
64
65 // Simuler un appel de méthode après 5 secondes
66 setTimeout(() => this.simulateSecurityAlert(), 5000);
67 },
68
69 // Vérifier l'état de sécurité
70 checkSecurityStatus: function() {
71 console.log(`[SURVEILLANCE] Statut du parc : ${this.status}, Niveau de sécurité : ${this.securityLevel}`);
72 },
73
74 // Simuler une alerte de sécurité
75 simulateSecurityAlert: function() {
76 console.log("SIMULATION : Brèche de sécurité détectée dans le secteur B !");
77
78 // Changer le statut du parc et le niveau de sécurité
79 this.status = "alerte";
80 this.securityLevel = "élevé";
81 console.log(`Statut du parc changé en : ${this.status}`);
82 console.log(`Niveau de sécurité relevé à : ${this.securityLevel}`);
83
84 // Vérification directe du statut
85 this.checkSecurityStatus();
86
87 // Créer une équipe d'intervention d'urgence à l'aide d'un constructeur
88 const responseTeam = new EmergencyResponseTeam(this);
89
90 // Passer une méthode comme callback tout en préservant le contexte
91 responseTeam.respondToAlert(this.resolveSecurityAlert.bind(this));
92 },
93
94 // Résoudre l'alerte de sécurité
95 resolveSecurityAlert: function(report) {
96 console.log(`Rapport reçu de l'équipe d'intervention : ${report}`);
97
98 // Rétablir le statut normal
99 this.status = "opérationnel";
100 this.securityLevel = "normal";
101 console.log("Alerte de sécurité résolue.");
102 console.log(`Statut du parc rétabli à : ${this.status}`);
103
104 // Vérifier le statut actuel
105 this.checkSecurityStatus();
106 },
107
108 // Arrêter la surveillance
109 stopMonitoring: function() {
110 if (this.monitoringInterval) {
111 clearInterval(this.monitoringInterval);
112 console.log("Surveillance du parc arrêtée.");
113 }
114 }
115};
116
117// Constructeur pour l'équipe d'intervention d'urgence
118function EmergencyResponseTeam(park) {
119 // 'this' désigne le nouvel objet
120 this.teamName = "Équipe d'intervention d'urgence";
121 this.members = ["Owen Grady", "Claire Dearing", "Dr. Wu"];
122 this.assignedPark = park;
123
124 this.respondToAlert = function(callback) {
125 console.log(`${this.teamName} intervient sur une alerte au parc : ${this.assignedPark.parkName}`);
126
127 // Simuler le temps de réponse
128 setTimeout(() => {
129 console.log(`${this.teamName} a sécurisé la zone et rétabli les clôtures.`);
130
131 // Appeler le callback avec un rapport
132 if (typeof callback === 'function') {
133 callback("Menace neutralisée, clôtures réparées");
134 }
135 }, 3000);
136 };
137}
138
139// Initialiser et démarrer le système
140parkManagementSystem.initialize();
141
142// Arrêter le système après 20 secondes
143setTimeout(() => {
144 parkManagementSystem.stopMonitoring();
145}, 20000);Récapitulatif et bonnes pratiques
Le mot-clé this est un outil puissant en JavaScript, mais il demande de comprendre comment il se comporte dans différents contextes :
Dans le contexte global :
thisvautwindowdans le navigateur etmodule.exports({}) dans un fichier Node.js ; dans une fonction classique hors mode strict, c'est l'objet global (globalThis).Dans une méthode d'objet :
thisdésigne l'objet qui a appelé la méthode.Dans un constructeur :
thisdésigne l'objet nouvellement créé.Dans les fonctions fléchées :
thisest hérité de la portée lexicale environnante.Avec call/apply/bind :
thisest explicitement spécifié par le programmeur.
Bonnes pratiques :
Utilise les fonctions fléchées pour les callbacks et les fonctions imbriquées afin de préserver le contexte
this.Utilise les méthodes
call(),apply()oubind()quand tu as besoin de contrôler explicitement la valeur dethis.Évite de créer des méthodes dans les constructeurs ; ajoute-les plutôt au prototype pour éviter la duplication de code.
Souviens-toi que
thisest déterminé au moment de l'appel de la fonction, pas au moment de sa définition.Utilise les classes ES6 au lieu des constructeurs traditionnels pour un code plus propre.
Dans la prochaine leçon, nous explorerons plus en profondeur les tableaux en JavaScript, qui sont un élément clé de la gestion des collections de données dans notre Jurassic Park.
Code de cette leçon : index.js
1// Mot-clé 'this' - Parc Jurassique
2console.log("Laboratoire du 'this' en JavaScript");
3console.log("Comprendre le contexte d'exécution\n");
4
5// ===========================================
6// 1. LES BASES DE 'this'
7// ===========================================
8console.log("=== 1. Qu'est-ce que 'this' ? ===");
9
10// 'this' fait référence à l'objet, dans le contexte qui exécute le code
11
12const dinosaur = {
13 name: "Tyrannosaurus Rex",
14 age: 8,
15
16 roar: function() {
17 console.log(`\n${this.name} rugit: ROAARRR!`);
18 console.log(` Âge: ${this.age} ans`);
19 }
20};
21
22dinosaur.roar(); // 'this' pointe vers l'objet 'dinosaur'
23
24// ===========================================
25// 2. 'this' DANS DIFFÉRENTS CONTEXTES
26// ===========================================
27console.log("\n=== 2. Contextes de 'this' ===");
28
29// Contexte 1: Méthode d'objet
30const raptor = {
31 name: "Velociraptor",
32 speed: 40,
33
34 run: function() {
35 console.log(`\n${this.name} court à une vitesse de ${this.speed} km/h`);
36 }
37};
38
39raptor.run(); // this = raptor
40
41// Contexte 2: Fonction globale (strict mode)
42function globalFunction() {
43 console.log("\nFonction globale - this:", typeof this);
44 // En strict mode: undefined
45 // Sans strict mode: window (navigateur) ou global (Node.js)
46}
47
48globalFunction();
49
50// Contexte 3: Constructeur
51function Dinosaur(name, species) {
52 this.name = name;
53 this.species = species;
54 this.roar = function() {
55 console.log(`${this.name} (${this.species}): ROAARRR!`);
56 };
57}
58
59const trex = new Dinosaur("Rexy", "T-Rex");
60console.log("\nConstructeur:");
61console.log(` Nom: ${trex.name}`);
62console.log(` Espèce: ${trex.species}`);
63trex.roar();
64
65// ===========================================
66// 3. PERTE DU CONTEXTE 'this'
67// ===========================================
68console.log("\n=== 3. Perte du contexte 'this' ===");
69
70const park = {
71 name: "Jurassic Park",
72 dinosaurs: ["T-Rex", "Velociraptor", "Triceratops"],
73
74 displayDinosaurs: function() {
75 console.log(`\nDinosaures dans ${this.name}:`);
76
77 // PROBLÈME: forEach avec une fonction traditionnelle
78 // this.dinosaurs.forEach(function(dino) {
79 // console.log(` - ${dino} de ${this.name}`); // this.name = undefined!
80 // });
81
82 // SOLUTION 1: Fonction fléchée
83 this.dinosaurs.forEach(dino => {
84 console.log(` - ${dino} de ${this.name}`);
85 });
86 }
87};
88
89park.displayDinosaurs();
90
91// SOLUTION 2: bind()
92const park2 = {
93 name: "Isla Nublar",
94 dinosaurs: ["Stegosaurus", "Brachiosaurus"],
95
96 displayDinosaurs: function() {
97 console.log(`\nDinosaures dans ${this.name}:`);
98
99 this.dinosaurs.forEach(function(dino) {
100 console.log(` - ${dino} de ${this.name}`);
101 }.bind(this)); // Liaison de this
102 }
103};
104
105park2.displayDinosaurs();
106
107// ===========================================
108// 4. MÉTHODES: call(), apply(), bind()
109// ===========================================
110console.log("\n=== 4. Méthodes: call(), apply(), bind() ===");
111
112const dinoInfo = {
113 describe: function(habitat, diet) {
114 console.log(`\n${this.name}:`);
115 console.log(` Environnement: ${habitat}`);
116 console.log(` Régime: ${diet}`);
117 }
118};
119
120const dino1 = { name: "T-Rex" };
121const dino2 = { name: "Triceratops" };
122
123// call() - appel avec un this explicite et des arguments
124dinoInfo.describe.call(dino1, "Forêt tropicale", "Carnivore");
125
126// apply() - comme call(), mais avec les arguments dans un tableau
127dinoInfo.describe.apply(dino2, ["Savane", "Herbivore"]);
128
129// bind() - crée une nouvelle fonction avec un this lié
130const describeTRex = dinoInfo.describe.bind(dino1);
131describeTRex("Jungle", "Carnivore");
132
133// ===========================================
134// 5. FONCTIONS FLÉCHÉES ET 'this'
135// ===========================================
136console.log("\n=== 5. Fonctions fléchées et 'this' ===");
137
138const enclosure = {
139 id: "A-7",
140 dinosaur: "Velociraptor",
141
142 // Méthode traditionnelle
143 checkTraditional: function() {
144 console.log(`\nEnclos ${this.id} (traditionnelle):`);
145
146 setTimeout(function() {
147 // console.log(` Dinosaure: ${this.dinosaur}`); // undefined ! this a changé
148 console.log(" this.dinosaur = undefined (perte de contexte)");
149 }, 100);
150 },
151
152 // Solution avec une fonction fléchée
153 checkArrow: function() {
154 console.log(`\nEnclos ${this.id} (fléchée):`);
155
156 setTimeout(() => {
157 console.log(` Dinosaure: ${this.dinosaur}`); // Fonctionne ! La fonction fléchée conserve this
158 }, 100);
159 }
160};
161
162enclosure.checkTraditional();
163
164setTimeout(() => {
165 enclosure.checkArrow();
166}, 150);
167
168// ===========================================
169// 6. 'this' DANS LES CONSTRUCTEURS ET LES CLASSES
170// ===========================================
171console.log("\n=== 6. 'this' dans les constructeurs et les classes ===");
172
173// Fonction constructeur
174function DinosaurPark(name) {
175 this.name = name;
176 this.dinosaurs = [];
177
178 this.addDinosaur = function(dino) {
179 this.dinosaurs.push(dino);
180 console.log(`${dino} ajouté à ${this.name}`);
181 };
182
183 this.getCount = function() {
184 return this.dinosaurs.length;
185 };
186}
187
188const park1 = new DinosaurPark("Parc Jurassique");
189park1.addDinosaur("T-Rex");
190park1.addDinosaur("Velociraptor");
191console.log(`Total des dinosaures: ${park1.getCount()}`);
192
193// Classe ES6
194class ModernPark {
195 constructor(name) {
196 this.name = name;
197 this.dinosaurs = [];
198 }
199
200 addDinosaur(dino) {
201 this.dinosaurs.push(dino);
202 console.log(`\n${dino} ajouté à ${this.name}`);
203 }
204
205 getCount() {
206 return this.dinosaurs.length;
207 }
208
209 displayAll() {
210 console.log(`\n${this.name} - Liste des dinosaures:`);
211 this.dinosaurs.forEach((dino, i) => {
212 console.log(` ${i + 1}. ${dino}`);
213 });
214 }
215}
216
217const park3 = new ModernPark("Isla Nublar");
218park3.addDinosaur("Triceratops");
219park3.addDinosaur("Stegosaurus");
220park3.addDinosaur("Brachiosaurus");
221park3.displayAll();
222console.log(`Total: ${park3.getCount()}`);
223
224// ===========================================
225// 7. PROBLÈME: MÉTHODE EN TANT QUE CALLBACK
226// ===========================================
227console.log("\n=== 7. Problème: méthode en tant que callback ===");
228
229const securitySystem = {
230 level: "ÉLEVÉ",
231
232 checkSecurity: function() {
233 console.log(`Niveau de sécurité: ${this.level}`);
234 }
235};
236
237// PROBLÈME: this sera perdu
238setTimeout(securitySystem.checkSecurity, 100); // this.level = undefined
239
240// SOLUTION 1: Fonction fléchée enveloppante (wrapper)
241setTimeout(() => {
242 securitySystem.checkSecurity();
243}, 150);
244
245// SOLUTION 2: bind()
246setTimeout(securitySystem.checkSecurity.bind(securitySystem), 200);
247
248// ===========================================
249// 8. EXEMPLE COMPLET
250// ===========================================
251console.log("\n=== 8. Système de gestion du parc - exemple complet ===");
252
253class JurassicParkManagement {
254 constructor(parkName) {
255 this.parkName = parkName;
256 this.dinosaurs = [];
257 this.visitors = 0;
258 this.status = "FERMÉ";
259 }
260
261 openPark() {
262 this.status = "OUVERT";
263 console.log(`\n${this.parkName} - Parc ouvert`);
264 }
265
266 closePark() {
267 this.status = "FERMÉ";
268 console.log(`\n${this.parkName} - Parc fermé`);
269 }
270
271 addDinosaur(name, species, sector) {
272 const dino = { name, species, sector, health: 100 };
273 this.dinosaurs.push(dino);
274 console.log(`${name} (${species}) ajouté au secteur ${sector}`);
275 }
276
277 addVisitors(count) {
278 this.visitors += count;
279 console.log(`Ajout de ${count} visiteurs. Total: ${this.visitors}`);
280 }
281
282 checkDinosaur(name) {
283 const dino = this.dinosaurs.find(d => d.name === name);
284 if (dino) {
285 console.log(`\nVérification de ${name}:`);
286 console.log(` Espèce: ${dino.species}`);
287 console.log(` Secteur: ${dino.sector}`);
288 console.log(` Santé: ${dino.health}%`);
289 } else {
290 console.log(`Dinosaure introuvable: ${name}`);
291 }
292 }
293
294 getDailyReport() {
295 console.log(`\nRAPPORT QUOTIDIEN - ${this.parkName}`);
296 console.log("=".repeat(50));
297 console.log(`Statut: ${this.status}`);
298 console.log(`Dinosaures: ${this.dinosaurs.length}`);
299 console.log(`Visiteurs: ${this.visitors}`);
300
301 if (this.dinosaurs.length > 0) {
302 console.log("\nListe des dinosaures:");
303 this.dinosaurs.forEach((dino, i) => {
304 console.log(` ${i + 1}. ${dino.name} (${dino.species}) - Secteur ${dino.sector}`);
305 });
306 }
307 }
308
309 // Méthode utilisant setTimeout avec une fonction fléchée
310 scheduleFeeding(dinoName, delay = 1000) {
311 console.log(`\n⏰ Repas programmé pour ${dinoName} dans ${delay}ms`);
312
313 setTimeout(() => {
314 const dino = this.dinosaurs.find(d => d.name === dinoName);
315 if (dino) {
316 console.log(`Nourrissage de ${dinoName} dans ${this.parkName}`);
317 dino.health = Math.min(100, dino.health + 10);
318 }
319 }, delay);
320 }
321}
322
323// Démonstration du système
324const management = new JurassicParkManagement("Isla Nublar");
325
326management.openPark();
327management.addDinosaur("Rexy", "Tyrannosaurus Rex", "A");
328management.addDinosaur("Blue", "Velociraptor", "B");
329management.addDinosaur("Trike", "Triceratops", "C");
330
331management.addVisitors(500);
332management.addVisitors(300);
333
334management.checkDinosaur("Blue");
335management.getDailyReport();
336
337management.scheduleFeeding("Rexy", 500);
338management.scheduleFeeding("Blue", 600);
339
340setTimeout(() => {
341 console.log("\n--- Après le repas ---");
342 management.checkDinosaur("Rexy");
343 management.checkDinosaur("Blue");
344
345 management.closePark();
346
347 console.log("\nFélicitations ! Tu maîtrises maintenant le mot-clé 'this' !");
348}, 800);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. Que signifie le mot-clé « this » en JavaScript ?
2. Vers quoi pointe « this » à l'intérieur d'une méthode d'objet en JavaScript ?
3. Comment se comporte « this » dans les fonctions fléchées ?
4. Dans quel contexte « this » pointe-t-il vers l'objet nouvellement créé ?