Leçon 2/6 11 min

this et les prototypes

this en JavaScript : pourquoi il change selon l'appelant, comment l'arrow function le capture, et les prototypes expliqués.

this dépend de QUI appelle

En JavaScript, this n'est pas figé : sa valeur dépend de comment la fonction est appelée, pas d'où elle est écrite. Dans une méthode appelée normalement (objet.methode()), this est l'objet. Mais si tu détaches la méthode (la passes en callback), this se perd.

class Compteur {
  valeur = 0;
  incr() { this.valeur++; console.log(this.valeur); }
}
const c = new Compteur();
c.incr();                  // 1  → this = c, OK

const f = c.incr;          // on DÉTACHE la méthode
// f();                    // ❌ this est undefined → erreur

setTimeout(c.incr, 100);   // ❌ même problème : appelé "tout seul"

C'est LE piège que tout débutant rencontre, surtout avec les addEventListener et les setTimeout.

La solution : les arrow functions capturent this

Une arrow function (=>) ne crée pas son propre this : elle garde celui de l'endroit où elle est écrite (this « lexical »). D'où le réflexe : entoure l'appel d'une arrow.

setTimeout(() => c.incr(), 100);   // ✅ l'arrow garde le contexte, c.incr() est bien appelé sur c

// Dans une classe, définir une méthode-champ en arrow fige aussi this :
class Bouton {
  clics = 0;
  onClick = () => { this.clics++; };   // this sera TOUJOURS le bouton
}

Les anciennes solutions .bind(c), .call(c), .apply(c) existent encore (elles forcent this), mais l'arrow function est aujourd'hui le réflexe le plus simple.

Pas de règle « toujours arrow ». L'arrow ne préserve pas this, elle n'en a pas et hérite de la portée d'écriture. Une seule question décide :

  • Tu l'appelles toi-même avec l'objet devant (c.incr(), une méthode d'objet) → fonction/méthode normale. this est déjà bon, et la méthode vit sur le prototype (partagée par toutes les instances).
  • Tu la passes détachée (addEventListener, .map, setTimeout) et elle a besoin de son thisarrow (() => c.incr()) ou champ-arrow. À noter : le champ-arrow est recréé pour chaque instance, donc à réserver à ce cas, pas à mettre partout par défaut.

Vois le piège (et le fix)

Essaie toi-même

Sous le capot : les prototypes

Avant les classes (2015), JavaScript faisait déjà de la POO… avec les prototypes. Chaque objet a un objet « parent » (son prototype) où JS va chercher une propriété ou une méthode s'il ne la trouve pas sur lui-même. C'est la chaîne de prototypes.

La vérité qui surprend : les classes JS ne sont que du « sucre syntaxique » par-dessus les prototypes. class Compte { deposer() {} } met en réalité deposer sur Compte.prototype, partagé par toutes les instances. Tu n'as pas besoin de manipuler les prototypes à la main (la syntaxe class est plus claire), mais savoir qu'ils sont là explique pourquoi l'héritage marche et pourquoi une méthode n'est pas dupliquée dans chaque objet.

À retenir : class = présentation moderne ; prototype = mécanisme réel en dessous. Comprendre ça, c'est ne plus jamais être surpris par le comportement des objets JS.

La chaîne de prototypes : quand on lit une propriété, JavaScript part de l'objet, puis remonte de prototype en prototype via __proto__ jusqu'à trouver, ou jusqu'à null. monObjet l'instance (ses données) titulaire · solde __proto__ Compte.prototype porte les méthodes partagées deposer() · retirer() __proto__ Object.prototype la racine de presque tout toString() · hasOwnProperty() __proto__ null
Pour lire une propriété, JS part de l'objet et remonte la chaîne via __proto__ jusqu'à la trouver, ou jusqu'à null.

this depends on WHO calls

In JavaScript, this isn't fixed: its value depends on how the function is called, not where it's written. In a method called normally (object.method()), this is the object. But if you detach the method (pass it as a callback), this gets lost.

class Compteur {
  valeur = 0;
  incr() { this.valeur++; console.log(this.valeur); }
}
const c = new Compteur();
c.incr();                  // 1  → this = c, OK

const f = c.incr;          // we DETACH the method
// f();                    // ❌ this is undefined → error

setTimeout(c.incr, 100);   // ❌ same problem: called "on its own"

It's THE trap every beginner hits, especially with addEventListener and setTimeout.

The fix: arrow functions capture this

An arrow function (=>) doesn't create its own this: it keeps the one from where it's written ("lexical" this). Hence the reflex: wrap the call in an arrow.

setTimeout(() => c.incr(), 100);   // ✅ the arrow keeps the context, c.incr() runs on c

// In a class, defining a field-method as an arrow also pins this:
class Bouton {
  clics = 0;
  onClick = () => { this.clics++; };   // this will ALWAYS be the button
}

The old solutions .bind(c), .call(c), .apply(c) still exist (they force this), but the arrow function is now the simplest reflex.

There's no "always use arrow" rule. An arrow doesn't preserve this, it has none and inherits the scope where it's written. One question decides:

  • You call it yourself with the object in front (c.incr(), an object method) → normal function/method. this is already correct, and the method lives on the prototype (shared across all instances).
  • You pass it detached (addEventListener, .map, setTimeout) and it needs its thisarrow (() => c.incr()) or arrow field. Note: an arrow field is recreated per instance, so save it for this case — don't sprinkle it everywhere by default.

See the trap (and the fix)

Try it yourself

Under the hood: prototypes

Before classes (2015), JavaScript already did OOP… with prototypes. Each object has a "parent" object (its prototype) where JS looks for a property or method if it can't find it on the object itself. That's the prototype chain.

The surprising truth: JS classes are just "syntactic sugar" over prototypes. class Compte { deposer() {} } actually puts deposer on Compte.prototype, shared by all instances. You don't need to handle prototypes by hand (the class syntax is clearer), but knowing they're there explains why inheritance works and why a method isn't duplicated in every object.

Takeaway: class = modern presentation; prototype = the real mechanism underneath. Understand that, and you'll never be surprised by JS object behavior again.

The prototype chain: when reading a property, JavaScript starts on the object, then climbs from prototype to prototype via __proto__ until it finds it, or reaches null. myObject the instance (its data) owner · balance __proto__ Account.prototype holds the shared methods deposit() · withdraw() __proto__ Object.prototype the root of almost everything toString() · hasOwnProperty() __proto__ null
To read a property, JS starts on the object and climbs the chain via __proto__ until it finds it, or hits null.

🎯 Pratique

S'entraîner (clique pour ouvrir) :

⚖️ Juge le code de l'IA
Accepter ou rejeter le code de l'IA

L'IA te propose ce composant : un compteur de clics qui branche sa méthode incr directement comme écouteur. Ton rôle de relecteur : accepter tel quel, ou rejeter, et dire pourquoi.

class Compteur {
  valeur = 0;
  incr() { this.valeur++; }
}
const c = new Compteur();
bouton.addEventListener("click", c.incr);
Rejeter. C'est exactement le piège de la leçon : c.incr passé seul est détaché de c. Au clic, addEventListener appelle la fonction avec this = l'élément DOM (le bouton), pas l'instance ; this.valeur++ écrit alors valeur sur le bouton et le vrai compteur ne bouge jamais. Le fix : envelopper dans une arrow, addEventListener("click", () => c.incr()), ou définir incr comme champ-arrow dans la classe pour figer this.
🧠 Rappel libre
Rappel libre

Sans remonter dans la leçon : de quoi dépend la valeur de this, pourquoi const f = c.incr; f() casse, et comment une arrow function répare le problème ?

this dépend de comment la fonction est appelée, pas d'où elle est écrite. c.incr() : this = c. Mais const f = c.incr; f() détache la méthode : elle est appelée "toute seule", donc this vaut undefined (mode strict des classes) et this.valeur lève une erreur. L'arrow () => c.incr() ne crée pas son propre this : à l'intérieur on rappelle explicitement c.incr(), donc this redevient c.
De quoi dépend la valeur de this dans une fonction JS ?
Pourquoi une arrow function aide avec this ?
Les classes JS, au fond, c'est…
Prochaine étape

Tes classes sont propres. Reste à découper ton code en fichiers réutilisables, au lieu d'un seul gros script : les ES Modules.

Leçon 3 : Les ES Modules →

Une erreur dans cette leçon, un passage flou, une question ? Écrivez-moi : chaque retour améliore ce cours.

Besoin d'un développeur pour votre projet ?

Réponse sous 24h · Sans engagement