Leçon 2/5 11 min

this et les prototypes

Le grand piège de JavaScript : à quoi se réfère this, pourquoi il « se perd » dans les callbacks, comment les arrow functions le capturent, et ce que sont vraiment les classes (du sucre sur les prototypes).

FR EN

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.

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

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.

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.
Next step

Your classes are clean. Now split your code into reusable files instead of one big script: ES Modules.

Lesson 3: ES Modules →
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.
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.
Besoin d'un développeur pour votre projet ?

Réponse sous 24h · Sans engagement