# Comprendre this en JavaScript

Que représente this en JavaScript

this est une propriété du contexte d’execution courant en JavaScript. Le contexte d’exécution est l’environnement dans lequel le code JavaScript est évalué et exécuté. Il existe trois types de contextes d’exécution en JavaScript:

Contexte d’exécution global: Le contexte initial dans lequel le code est exécuté lors du premier chargement du script.

Contexte d’exécution fonctionnel: À chaque fois qu’une fonction est appelée, un nouveau contexte d’exécution est créé pour cette fonction. Dans ce contexte, this dépend de la manière dont la fonction a été appelée.

Contexte d’exécution d’évaluation: Le contexte d’exécution d’évaluation est créé lorsqu’une chaîne de code est évaluée à l’aide de la fonction eval(). La valeur de this dans ce contexte dépend du code évalué.

De plus la valeur de this peut être modifiée à l’aide des méthodes call, apply et bind. Cela permet d’ajuster la valeur de this dans un contexte d’exécution.

this en contexte d’execution global

Le contexte d’excution est le code qu’on exécute au premier niveau de notre script. la valeur de this est l’objet window dans un navigateur ou l’objet global dans node js.

<script>
console.log(this); // Dans le context d'execution global [object Window]
function maFonction(){
	  console.log("n'est plus dans le contexte d'exécution global");
      console.log(this); // affiche [object Window]
});
maFonction();
</script>

Le second console.log(this) est exécuté au sein de la fonction maFonction, l’évaluation de this se fait sur le contexte fonctionnel de maFonction. this vaut également window ou global dans ce cas. En effet nous allons voir par la suite que dans le contexte fonctionnel this peut valoir window.

this en contexte fonctionnel

Dans une fonction, this dépend de la manière dont la fonction est appelée. Dans un contexte d’exécution fonctionnel (au sein d’une fonction) si une fonction simple (non liée à un objet ou un contexte spécifique) est appelée, this prendra par défaut la valeur de l’objet global, qui est window dans un navigateur ou global dans node.js.

this dans une fonction simple

function maFonction() {
  console.log(this);
}

maFonction(); // affiche l'objet 'window' ou 'global'

this dans les méthodes d’objet

Dans une méthode d’objet, this fait référence à l’objet qui contient la méthode.

const monObjet = {
  nom: "Neo",
  description: function() {
    console.log(`Mon nom est ${this.nom}`);
  }
};
monObjet.description(); // affiche "Mon nom est Neo"

En revanche dans l’exemple ci-dessous on assigne la fonction description de l’objet dans une variable descriptionFn puis on l’invoque:

const monObjet = {
  nom: "Neo",
  description: function() {
    console.log(`Mon nom est ${this.nom}`);
  }
};
const descriptionFn = monObjet.description;
descriptionFn(); // affiche "Mon nom est undefined"

La fonction descriptionFn n’est plus une fonction de l’objet monObjet lorsqu’elle est invoquée par conséquent la valeur de this est window et this.nom vaut undefined.

this dans un constructeur

Une fonction constructeur est la fonction d’inialisation d’un nouvel objet il va de soi que la valeur de this sera le nouvel objet créé.

function Personne(nom, age) {
  this.nom = nom;
  this.age = age;
}

const personne1 = new Personne("Julia", 25);
console.log(personne1.nom); // affiche "Julia"

Ainsi lorsque l’on exécute const personne1 = Personne(“Julia”, 25), this dans le constructeur fait référence à l’objet qui sera dans la variable personne1. Objet qui est au départ vide et auquel on lui ajoute différentes propriétés via le constructeur.

this dans une classe

A l’image des fonctions constructeurs, les classes ES6 permettent d’instancier des nouveaux objets via le mot clé new. De la même façon il est possible d’utiliser this dans les méthodes et ce dernier se réfère à la nouvelle instance créée.

class Personne {
  nom;
  constructor(nom) {
 	this.nom = nom;
  }

 afficherNom() {
 	console.log(this.nom);
 }
}

const personne = new Personne("Théo");
console.log(personne.afficherNom()); // Théo

Configuration de this avec bind, call, apply

Comme vu précédemment la valeur this dépend du contexte d’exécution. Dans certains cas cette valeur pré-définie ne correspond pas et il est possible de la configurer.

Utilisation de bind

bind permet lors de la définition d’une fonction de définir la valeur de this. Reprenons l’exemple suivant:

const monObjet = {
  nom: "Neo",
  description: function() {
    console.log(`Mon nom est ${this.nom}`);
  }
};
const descriptionFn = monObjet.description;
descriptionFn(); // affiche "Mon nom est undefined"

Pour que descriptionFn puisse afficher le nom de l’objet monObjet, il faut que this à l’intérieur de descriptionFn fasse référence à monObjet. Pour ce faire, nous pouvons utiliser la méthode bind() en passant monObjet en paramètre.

const monObjet = {
  nom: "Neo",
  description: function() {
    console.log(`Mon nom est ${this.nom}`);
  }
};
const descriptionFn = monObjet.description.bind(monObjet);
descriptionFn(); // affiche "Mon nom est Neo"

bind permet de spécifier la valeur de this lors de la définition d’une fonction. Il est très courant de vouloir changer la valeur de this à l’invocation d’une fonction sans pour autant définir une nouvelle fonction. Ceci est possible via call et apply.

Utilisation de call

La méthode call permet d’appeler une fonction en spécifiant la valeur de this. Le premier argument est la valeur de this, les autres arguments sont n arguments n étant les arguments de la fonctions originale.

function.call(thisArg, arg1, arg2, ..., argN);

Exemple:

const monObjet = {
  nom: "Neo",
  description: function(lang) {
  if (lang === 'fr')  {
    console.log(`Mon nom est ${this.nom}`);
    } else {
    console.log(`My name is ${this.nom}`);
    }
  }
};
const descriptionFn = monObjet.description;
descriptionFn.call(monObjet, 'fr'); // affiche "Mon nom est Neo"
descriptionFn.call(monObjet, 'en'); // affiche "My name is Neo"

Utilisation de apply

La méthode apply permet d’appeler une fonction en spécifiant la valeur de this. Elle est similaire à call. Le premier argument est la valeur de this, à la différence de call le deuxième argument est un tableau d’arguments de la fonction originale.

function.apply(thisArg, [arg1, arg2, ..., argN]);

Exemple:

const monObjet = {
  nom: "Neo",
  description: function(lang) {
  if (lang === 'fr')  {
    console.log(`Mon nom est ${this.nom}`);
    } else {
    console.log(`I am ${this.nom}`);
    }
  }
};
const descriptionFn = monObjet.description;
descriptionFn.apply(monObjet, ['fr']); // affiche "Mon nom est Neo"
descriptionFn.apply(monObjet, ['en']); // affiche "I am Neo"

this dans une fonction fléchée

En utilisant une fonction fléchée pour définir une fonction, la valeur de this n’est plus déterminée dynamiquement lors de l’appel de la fonction, mais plutôt déterminée lexicalement lors de la définition de la fonction. La valeur de this dans une fonction fléchée est déterminée par le contexte entourant la fonction fléchée au moment de sa définition, plutôt que par le contexte dans lequel la fonction est appelée. Autrement dit la valeur de this dans une fonction fléchée sera la même que celle de la portée parente où elle a été définie. Voici un exemple qui compare l’utilisation d’une fonction simple et une fonction fléchée.

Fonction simple:

const monObjet = {
  nom: "Neo",
  description: function() {
    setTimeout(function(){
    console.log(`Mon nom est ${this.nom}`);
    }, 1000);
  }
};

monObjet.description(); // Mon nom est undefined

Fonction fléchée:

const monObjet = {
  nom: "Neo",
  description: function() {
    setTimeout(() => {
    console.log(`Mon nom est ${this.nom}`);
    }, 1000);
  }
};

monObjet.description(); // Mon nom est Neo

Dans le premier exemple on utilise une fonction simple dans un setTimeout. la fonction est sans contexte particulier, ne provient pas d’un objet: this dans cette fonction a pour valeur window. On accède ensuite à la propriété nom qui n’existe pas sur window d’où undefined. Dans le second exemple on utilise une fonction fléchée dans un setTimeout. La valeur de this est le this de la fonction qui l’a créée à savoir le this de monObjet.description. La valeur de this de monObjet.description est monObjet, la fonction fléchée reprend cette valeur pour son this et peut afficher la propriété nom. L’utilisation des fonctions fléchés est aujourd’hui recommandée car elles sont plus claires et prévisibles.

this dans addEventListener

En JavaScript il est possible d’écouter des évènements sur des éléments HTML via addEventListener(). Cettte méthode prend en premier argument le type d’évènement et en second argument une fonction. Dans cette fonction this représente l’élement HTML sur lequel on veut écouter les évènements.

const boutonElement = document.getElementById('monButton');
boutonElement.addEventListener('click', function() {
    this.textContent = 'Cliqué!';
    console.log(this); // boutonElement
});

Pourtant on a vu qu’une fonction anonyme sans contexte qui ne fait pas partie d’un objet a son this sur l’objet window. Lors de l’exécution de la fonction anonyme, la méthode addEventListener se charge de lier le mot-clé this à l’élément HTML sur lequel l’événement est déclenché.

this dans un eval

La valeur de this au sein d’une instruction eval() est le this du code qui a invoqué l’eval.

const monObjet = {
  nom: "Neo",
  afficherNom: function() {
    eval(`console.log(this.nom)`);
  }
};

monObjet.afficherNom();  // Neo

Les même règles sur this s’appliquent quand on définit une fonction au sein de eval().

const monObjet = {
  nom: "Neo",
  afficherNom: function() {
    eval(`function afficherNom2() {
            return this.nom;
        }
        afficherNom2();
    `);
  }
};

monObjet.afficherNom();  // undefined (this vaut window dans afficherNom2)
const monObjet = {
  nom: "Neo",
  afficherNom: function() {
    eval(`const afficherNom2 = () => console.log(this.nom);
        afficherNom2();
    `);
  }
};

monObjet.afficherNom();  // Neo

L’utilisation de eval() reste peu recommmandé il est important de l’utiliser avec précaution.