Menu
Français

Mot-clé this en JavaScript : règles et pièges

Comprendre comment this fonctionne vraiment en JavaScript : les quatre règles de liaison, le cas particulier des fonctions fléchées, et comment éviter le fameux piège du this is undefined.

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 :

index.js
Output
Click Run to see the output here.

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 :

index.js
Output
Click Run to see the output here.

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 :

index.js
Output
Click Run to see the output here.

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 :

index.js
Output
Click Run to see the output here.

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 :

index.js
Output
Click Run to see the output here.

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

index.js
Output
Click Run to see the output here.

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 :

index.js
Output
Click Run to see the output here.

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 :

index.js
Output
Click Run to see the output here.

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 :

index.js
Output
Click Run to see the output here.

setTimeout exécute la fonction comme un simple appel, pas comme c.increment(). Trois façons de corriger ça :

index.js
Output
Click Run to see the output here.

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) : this vaut window.
  • Module ES (ce qui couvre la plupart du code moderne passé par un bundler) : this vaut undefined.
  • Module CommonJS sous Node.js : this correspond à module.exports.

Pour obtenir une référence fiable à l'objet global quel que soit l'environnement, on utilise globalThis :

index.js
Output
Click Run to see the output here.

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 :

  1. Est-ce une fonction fléchée ? Alors this vaut ce qu'il valait dans la portée englobante. Le site d'appel n'a aucune importance.
  2. La fonction a-t-elle été appelée avec new ? Dans ce cas, this est le nouvel objet créé.
  3. Est-elle appelée via .call, .apply ou une fonction liée avec bind ? Alors this correspond à la valeur passée.
  4. Est-ce un appel du type obj.method() ? Dans ce cas, this vaut obj.
  5. Est-ce un simple appel fn() ? Alors this est undefined en 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 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 ?

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 ?

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 ?

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.

Apprendre à coder avec Coddy

COMMENCER