this se décide au moment de l'appel
La plupart des confusions autour du mot-clé this en JavaScript viennent d'une idée fausse : croire que this dépendrait de l'endroit où la fonction est définie. Ce n'est pas le cas. this dépend de la manière dont la fonction est appelée.
Regarde la même fonction appelée de deux façons différentes :
Même fonction, mais this change. Au premier appel, user se trouve avant le point : this vaut donc user. Au second appel, il n'y a rien avant le nom de la fonction, et this devient undefined. La fonction, elle, n'a pas bougé d'un poil — c'est le site d'appel qui a changé.
Gardez bien ça en tête : pour savoir ce que vaut this, regardez l'appel, pas la définition.
Les quatre règles de binding de this en JavaScript
Il existe quatre façons dont this est défini. Quasiment toutes les questions que vous vous poserez un jour sur this se résoudront en identifiant laquelle de ces quatre règles s'applique.
1. Appel sous forme de méthode : obj.fn()
Lorsqu'une fonction est appelée comme propriété d'un objet, this correspond à cet objet :
Ce qui se trouve avant le point devient this. C'est aussi simple que ça.
2. Appel classique : fn()
Quand la fonction est appelée sans objet devant, this vaut undefined en mode strict (activé automatiquement dans les modules et les classes) ou correspond à l'objet global en mode non-strict :
C'est exactement là que surgit le fameux « this is undefined ». Dès que tu détaches une méthode de son objet, l'appel de méthode devient un simple appel de fonction :
Sans counter. devant l'appel, aucun binding. La fonction ne garde aucun souvenir de l'objet d'où elle vient.
3. Binding explicite : .call(), .apply() et .bind()
Tu peux forcer la valeur de this à ce que tu veux :
.call et .apply exécutent la fonction immédiatement ; la seule différence, c'est la façon de passer les arguments. .bind, lui, renvoie une nouvelle fonction dans laquelle this est verrouillé une bonne fois pour toutes — pratique pour les callbacks.
4. Appel avec new : new Fn()
Quand on appelle une fonction avec new, JavaScript crée un nouvel objet et le lie à this :
Les classes s'appuient sur ce mécanisme en coulisses. On les abordera dans un chapitre ultérieur.
Les fonctions fléchées n'ont pas leur propre this
Les fonctions fléchées cassent volontairement les règles vues plus haut. Elles ne lient pas du tout this : elles l'héritent de la portée englobante, figé au moment où la fonction fléchée est définie :
La fonction fléchée déclarée au niveau racine d'un module capture le this du module, qui vaut undefined. On a beau l'appeler via user.arrow(), la fonction fléchée refuse de relier son this.
Ça ressemble à un bug, mais c'est justement tout l'intérêt. Les fonctions fléchées prennent tout leur sens à l'intérieur des méthodes, quand on veut conserver le this du contexte englobant :
La fonction fléchée à l'intérieur de setInterval hérite de this depuis start, qui a été appelée via timer.start(). Du coup, this.seconds fonctionne. Une fonction classique (function), elle, aurait eu son propre this (celui que setInterval lui aurait filé) et tout aurait cassé.
La règle à retenir : les fonctions fléchées pour les callbacks à l'intérieur des méthodes, et les fonctions classiques pour les méthodes elles-mêmes.
Le piège classique du callback
C'est de loin la façon la plus courante de se faire piéger par this en JavaScript. Tu passes une méthode en tant que callback, et hop, elle perd son binding :
setTimeout exécute la fonction comme un simple appel, pas comme c.increment(). Trois façons de corriger ça :
Les trois versions marchent. Le wrapper avec la fonction fléchée reste en général le plus lisible.
this au niveau global
La valeur de this au niveau racine dépend de l'environnement d'exécution :
- Script de navigateur (hors module) :
thisvautwindow. - Module ES (ce qui couvre la plupart du code moderne passé par un bundler) :
thisvautundefined. - Module CommonJS sous Node.js :
thiscorrespond àmodule.exports.
Pour obtenir une référence fiable à l'objet global quel que soit l'environnement, on utilise globalThis :
En pratique, mieux vaut éviter de s'appuyer sur this au niveau global. Quand tu as vraiment besoin de l'objet global, utilise globalThis ; sinon, transmets explicitement les valeurs dont tu as besoin.
Un petit arbre de décision
Quand tu tombes sur un this et que tu ne sais pas ce qu'il vaut, déroule cette liste dans l'ordre :
- Est-ce une fonction fléchée ? Alors
thisvaut ce qu'il valait dans la portée englobante. Le site d'appel n'a aucune importance. - La fonction a-t-elle été appelée avec
new? Dans ce cas,thisest le nouvel objet créé. - Est-elle appelée via
.call,.applyou une fonction liée avecbind? Alorsthiscorrespond à la valeur passée. - Est-ce un appel du type
obj.method()? Dans ce cas,thisvautobj. - Est-ce un simple appel
fn()? Alorsthisestundefineden mode strict.
Cet enchaînement, dans cet ordre précis, tranche tous les cas.
La suite : les fonctions d'ordre supérieur
Maintenant que this n'a plus de secret pour toi, tu es prêt pour le concept qui fait toute la richesse de JavaScript : manipuler les fonctions comme des valeurs. On va s'attaquer aux fonctions d'ordre supérieur — celles qui prennent ou renvoient d'autres fonctions — et voir comment elles sont au cœur des méthodes de tableau, des gestionnaires d'événements et de la majorité du code JavaScript qu'on écrit au quotidien.
Questions fréquentes
À quoi fait référence this en JavaScript ?
this en JavaScript ?this fait référence à l'objet sur lequel la fonction est appelée, pas à l'endroit où elle a été définie. Dans user.greet(), this vaut user. Si on appelle simplement greet(), this vaut undefined en mode strict (ou l'objet global en mode non strict). Ce qui compte, c'est le site d'appel, pas la définition.
Pourquoi this est-il undefined dans ma fonction ?
this est-il undefined dans ma fonction ?Généralement parce que vous avez détaché une méthode de son objet pour l'appeler seule, ou que vous l'avez passée en callback. const fn = user.greet; fn(); perd la liaison : il n'y a plus rien à gauche du point au moment de l'appel. Pour corriger ça, utilisez .bind(user), enveloppez l'appel dans une arrow function, ou appelez directement user.greet().
En quoi this est-il différent dans les fonctions fléchées ?
this est-il différent dans les fonctions fléchées ?Les arrow functions n'ont pas leur propre this. Elles héritent du this du contexte englobant au moment où elles sont définies. C'est parfait pour les callbacks à l'intérieur d'une méthode, quand on veut conserver le this extérieur. Conséquence : .call(), .apply() et .bind() ne peuvent pas modifier le this d'une fonction fléchée.
Que vaut this au niveau racine d'un script ?
this au niveau racine d'un script ?Dans un script classique côté navigateur, le this global correspond à l'objet window. Dans un module ES, il vaut undefined. Dans un module CommonJS Node.js, il pointe vers module.exports. La référence portable entre tous les environnements est globalThis, qui désigne toujours l'objet global.