Qu’est-ce que le paramètre “this” en JavaScript

La this Le paramètre en JavaScript est comme cet obstacle étrangement dissimulé sur un chemin qui ne cesse de faire trébucher les gens. Pour le débutant JavaScript, c’est souvent une chose peu claire, alors que pour beaucoup de développeurs assez expérimentés, c’est l’une de ces choses qu’ils ont compris comment utiliser mais n’ont jamais vraiment entendu.

La this Le paramètre est un ingrédient essentiel du JavaScript orienté objet et une compréhension approfondie de son comportement est essentielle pour débloquer tant d’autres concepts. À la fin de cet article, vous devriez avoir obtenu les informations nécessaires pour dissiper à jamais toute incertitude que vous pourriez avoir sur this sujet (jeu de mots si voulu).

Lorsqu’une fonction est appelée, en plus des arguments explicites qui lui sont transmis, elle reçoit d’autres paramètres implicites sous le capot qui sont accessibles dans le corps de la fonction. L’un d’eux est le this paramètre qui représente l’objet associé à l’invocation de la fonction. On l’appelle souvent le contexte de la fonction.

Cependant, this Et la façon dont il est déterminé est l’une de ces choses en JavaScript qui ne sont pas si simples. La valeur de this n’est pas seulement dicté par comment et où une fonction est définie, mais aussi, en grande partie, par la façon dont elle est invoquée. Pour commencer à faire face ou face à la façon dont this dans une fonction est déterminée, nous devons revoir notre connaissance du comportement de la fonction.

Il existe quatre façons d’invoquer des fonctions en JavaScript, chacune avec sa propre particularité :

  1. En tant que fonction-averageJoe()dans lequel la fonction est invoquée de manière directe
  2. Comme méthode—averageJoe.talk()qui lie l’invocation à un objet, permettant la programmation orientée objet
  3. En tant que constructeur—new AverageJoe()dans lequel un nouvel objet est créé
  4. Via les méthodes apply ou call de la fonction—averageJoe.call(someObject) ou averageJoe.apply(someObject)

— Les secrets du ninja JavaScript, deuxième édition

En tant que fonction

Lorsqu’une fonction est invoquée de manière simple, son contexte de fonction (c’est-à-dire le this valeur) peut être deux choses. En mode non strict, le contexte global (le window objet) devient le contexte de la fonction. En mode strict, ce sera undefined.

function averageJoe() {
    console.log(this)
}

function strictJoe() {
    "use strict"
    console.log(this)
}

averageJoe()  //  window object
strictJoe()  //  undefined

Le résultat est le même même si la fonction est définie dans une fonction, tant qu’elle est invoquée de manière simple :

function outer() {
  function inner() {
    console.log(this)
  }

  function strictInner() {
    "use strict"
    console.log(this)
  }

  inner()  //  window object
  strictInner()  //  undefined
}
outer()

En tant que méthode

Les fonctions peuvent également être des propriétés dans des objets et à ce titre, elles sont appelées méthodes. Lorsqu’une fonction est invoquée via un objet (en tant que méthode), l’objet lui-même devient le contexte de la fonction et est accessible dans le corps de la méthode via le this paramètre.

averageJoe = {
  name: "Joe",
  talk: function() {
    console.log(this)
  }
}

averageJoe.talk()  //  {name: "Joe", talk: ƒ}

En tant que constructeur

Les fonctions de constructeur sont essentiellement les mêmes anciennes fonctions courantes que nous avons traitées. Comme leur nom l’indique, nous les utilisons pour “construire” de nouveaux objets. Pour invoquer une fonction en tant que constructeur, nous précédons l’invocation de la fonction avec le new mot-clé.

Lorsqu’une fonction est invoquée avec le new , une nouvelle instance d’objet est créée et fournie à la fonction en tant que contexte. Dans la fonction, toute référence à this est une référence à l’instance d’objet nouvellement créée.

function AverageJoe() {
  console.log(this)  //  {} 'new object'
  this.name = "Joe"
  console.log(this)  //  {name: "Joe"}
}
new AverageJoe()

Via la méthode apply ou call

Les fonctions en JavaScript ont accès à deux méthodes intégrées : apply et call.

func.apply(thisArg, argArray)

func.call(thisArg, arg1, arg2, ...)

Ils nous permettent d’invoquer une fonction et de la lier explicitement à un objet. Tout objet fourni au thisArg devient le contexte de la fonction et ce qui est référencé par this au sein de la fonction.

let averageJoe = {
  name: "Joe"
}

function randomGuy() {
  console.log(this)
}

randomGuy.call(averageJoe)  //  {name: "Joe"}

Les fonctions étant des objets de première classe en JavaScript, elles peuvent, entre autres, être affectées à des objets et transmises comme d’autres types de valeur. Bien sûr, étant des objets, lorsque nous les attribuons ou les transmettons, ce que nous transmettons en réalité est leur référence.

Cette flexibilité autour des fonctions crée une grande variété dans la manière dont elles sont appliquées et utilisées. Voyons comment les concepts que nous avons couverts jusqu’à présent entrent en jeu dans certains de ces scénarios.

function loneGuy() {
  console.log(this)
}
loneGuy()  //  window object


let averageJoe = {
  name: "Joe",
  talk: loneGuy
}
averageJoe.talk()  //   {name: "Joe", talk: ƒ}


let anotherAverageJoe = {
  name: "Another Joe",
  speak: averageJoe.talk
}
anotherAverageJoe.speak()  //  {name: "Another Joe", speak: ƒ}


let anotherLoneGuy = anotherAverageJoe.speak
anotherLoneGuy()  //  window object


anotherLoneGuy.apply(averageJoe)  //  {name: "Joe", talk: ƒ}
averageJoe.talk.call(anotherAverageJoe)  //  {name: "Another Joe", speak: ƒ}

On commence par définir une fonction loneGuy qui enregistre la valeur actuelle de this dans son corps de fonction :

function loneGuy() {
  console.log(this)
}

Lorsqu’il est invoqué comme une fonction ordinaire et autonome, le window l’objet est sorti comme la valeur de this:

loneGuy()  //  window object

Nous continuons à créer un objet qui a un talk méthode qui fait référence à la loneGuy fonction. Quand le talk méthode est invoquée, son objet parent, averageJoe devient maintenant le this évaluer:

let averageJoe = {
  name: "Joe",
  talk: loneGuy
}
averageJoe.talk()  //   {name: "Joe", talk: ƒ}

Nous créons un autre objet anotherAverageJoe à qui speak méthode est une référence à averageJoe.talk. La speak la méthode est invoquée via son objet parent anotherAverageJoequi est correctement sorti comme le this évaluer.

let anotherAverageJoe = {
  name: "Another Joe",
  speak: averageJoe.talk
}
anotherAverageJoe.speak()  //  {name: "Another Joe", speak: ƒ}

Nous créons une nouvelle variable anotherLoneGuy et passez-lui une référence à anotherAverageJoe.speak. Nous allons de l’avant pour l’invoquer d’une manière simple et bien sûr, il obtient le window objet comme son this évaluer.

let anotherLoneGuy = anotherAverageJoe.speak
anotherLoneGuy()  //  window object

Ensuite, nous invoquons le nouveau anotherLoneGuy via le module intégré apply méthode et fournir explicitement averageJoe comme contexte de sa fonction. Comme prévu, il s’exécute et se connecte averageJoe comme sa this évaluer. Nous invoquons également averageJoe.talk via le call méthode et fournir anotherAverageJoe comme son contexte de fonction qu’il produit dûment comme son this valeur, bien qu’il s’agisse d’une méthode averageJoe.

anotherLoneGuy.apply(averageJoe)  //  {name: "Joe", talk: ƒ}
averageJoe.talk.call(anotherAverageJoe)  //  {name: "Another Joe", speak: ƒ}

De tous les passages et réaffectations de notre fonction initiale, nous pouvons voir que si où et comment une fonction est définie peuvent avoir une influence sur la façon dont elle this valeur est atteinte, la façon dont elle est finalement invoquée est le facteur le plus déterminant.

Les fonctions fléchées sont venues avec ES6 et ont apporté une nouvelle élégance à la façon dont les fonctions étaient utilisées en JavaScript. Ils ont rejeté une partie du bagage syntaxique des fonctions traditionnelles et ont permis aux fonctions d’être exprimées de manière plus succincte et lucide.

Cependant, les expressions de fonction fléchée n’étaient pas seulement une refonte syntaxique des fonctions traditionnelles. Ils différaient non seulement par leur syntaxe, mais également par leur comportement, l’un d’entre eux étant la manière dont le contexte de la fonction est déterminé. Les fonctions fléchées n’ont pas les leurs this évaluer. Au lieu de cela, ils se souviennent de la valeur du this paramètre au moment de leur définition.

Comprenons cela en parcourant un peu de code.

function randomGuy() {
  function regularFunc() {
    console.log(this)
  }

  const arrowFunc = () => {
    console.log(this)
  }

  regularFunc()
  arrowFunc()
}
randomGuy()
//  regularFunc –> window object
//  arrowFunc –> window object


let averageJoe = {
  name: "Joe",
  talk: randomGuy
}
averageJoe.talk()
//  regularFunc –> window object
//  arrowFunc –> {name: "Joe", talk: ƒ}

Pour commencer, nous définissons un randomGuy fonction, à l’intérieur de laquelle nous logeons deux autres fonctions – une fonction normale regularFunc et une expression de fonction fléchée arrowFunc— qui enregistrent tous deux la valeur de this à l’intérieur de leurs corps respectifs.

function randomGuy() {
  function regularFunc() {
    console.log(this)
  }

  const arrowFunc = () => {
    console.log(this)
  }

  regularFunc()
  arrowFunc()
}

Nous invoquons randomGuy la manière simple et son contexte de fonction devient le window objet. Le code s’exécute au-delà des définitions de fonction et atteint le regularFunc invocation. Il est également invoqué de manière simple, ainsi, il obtient le window objet comme son contexte de fonction également. Prochain, arrowFunc est invoquée et comme une fonction fléchée qui ne détermine pas sa propre this valeur, il prend la this valeur existant au moment de sa définition, qui était la window objet.

randomGuy()
//  regularFunc –> window object
//  arrowFunc –> window object

Il est essentiel de comprendre la nuance ici. Même si les deux fonctions se sont terminées par le window objet comme leurs respectifs this valeurs, les raisons étaient différentes. Pour regularFunc c’est parce qu’il a été invoqué d’une manière directe, alors que pour arrowFunc c’était parce que le window l’objet était l’existant this valeur au moment de sa définition et c’est avec cela qu’il est resté.

Passons à la définition d’un objet averageJoe qui a un talk méthode qui fait référence à randomGuy.

let averageJoe = {
  name: "Joe",
  talk: randomGuy
}

Quand le talk méthode est invoquée, la this la valeur dans son corps devient son objet parent averageJoe par lequel il a été invoqué. Nous allons au-delà des définitions de fonctions et encore une fois, regularFunc est invoqué d’une manière simple, ce qui rend le window objecter son this évaluer. Prochain, arrowFunc est invoquée, et étant une fonction fléchée, elle mémorise le this valeur qui existait au moment où elle a été définie (la averageJoe objet) et en hérite comme sien this évaluer.

averageJoe.talk()
//  regularFunc –> window object
//  arrowFunc –> {name: "Joe", talk: ƒ}

Les fonctions fléchées obtiennent leur contexte de fonction à partir du contexte de fonction existant au moment de leur définition. Ils se souviennent de ce contexte et s’y tiennent fidèlement, peu importe comment ils sont invoqués plus tard.

Les flèches fonctionnent comme des rappels

Un domaine où l’aspect pratique des fonctions fléchées entre en jeu est l’utilisation des fonctions de rappel. Les fonctions traditionnelles ont toujours été excentriques à cet égard et avant les fonctions fléchées, les développeurs devaient recourir à des solutions de contournement lorsqu’ils les utilisaient comme rappels dans certains cas. Jetez un oeil à ce code par exemple:

let averageJoe = {
  hobbies: ["reading", "coding", "blogging"],
  printHobby: function(hobby) {
    console.log(hobby)
  },
  printHobbies: function() {
    this.hobbies.forEach(function(hobby) {
      this.printHobby(hobby)
    })
  }
}
averageJoe.printHobbies()  //  Uncaught TypeError: this.printHobby is not a function

Dans ce scénario, l’utilisation d’une expression de fonction traditionnelle comme rappel ne nous a apporté que du chagrin. Lorsque la fonction de rappel anonyme que nous avons transmise au forEach méthode est invoquée, elle prend la window objet comme son contexte de fonction et qui, bien sûr, n’est pas là où le printHobby méthode réside. D’où l’erreur que nous avons eue.

Cependant, lorsque nous utilisons une fonction fléchée à la place, nous voyons qu’elle capture le this valeur au moment de sa définition (le averageJoe objet) et cela, à son tour, conduit à la sortie que nous désirons :

// ...

  printHobbies: function() {
    this.hobbies.forEach(hobby => {
      this.printHobby(hobby)
    })
  }
}
averageJoe.printHobbies()  //  "reading", "coding", "blogging"

Utilisation des fonctions fléchées comme méthodes

Vous devriez être un peu prudent avec les fonctions fléchées. Leur caractéristique d’hériter de la this paramètre conduit à une bizarrerie lorsque nous les utilisons comme méthodes.

Prenez ceci par exemple :

let averageJoe = {
  name: "Joe",
  talk: () => {
    console.log(this)
   }
}

averageJoe.talk()  //  window object

Au sein de la talk méthode, this fait maintenant référence au window objet par opposition à son objet parent averageJoe. Fou, non? Pas de panique, je vais vous expliquer.

C’est en fait assez simple. Les fonctions fléchées collent toujours aux this valeur existant au moment de leur définition signifie qu’ils ne prendront pas leurs objets parents comme contexte de fonction lorsqu’ils seront invoqués via eux en tant que méthodes. Dans ce cas particulier, averageJoe est créé dans le code JavaScript global (c’est-à-dire pas dans une fonction) où la valeur de this est le window objet et c’est ce que l’expression de la fonction fléchée a utilisé pour l’objet talk méthode coincé avec.

Ce même comportement des fonctions fléchées conduit à un résultat différent en ce qui concerne les fonctions constructeur :

function AverageJoe() {
  this.name = "Joe"
  this.talk = () => {
      console.log(this)
    }
}

let averageJoe = new AverageJoe()
averageJoe.talk()  //  {name: "Joe", talk: ƒ}

Comme nous le savons, invoquer des fonctions constructeur avec la new mot clé conduit à la création d’une nouvelle instance d’objet et fait le contexte de la fonction. Donc, en substance, l’objet auquel nous transmettons la fonction de flèche en tant que méthode est également le contexte de la fonction existante au moment où il a été défini et c’est ce à quoi il s’en tiendra chaque fois qu’il sera invoqué.

En réalité, toutefois il est invoqué.

averageJoe.talk()  //  {name: "Joe", talk: ƒ}

let loneGuy = averageJoe.talk
loneGuy()  //  {name: "Joe", talk: ƒ}  not window object

averageJoe.talk.call(window)  //  {name: "Joe", talk: ƒ}  still not window object

let anotherLoneGuy = averageJoe.talk.bind(window)
anotherLoneGuy()  //  {name: "Joe", talk: ƒ}  lol, still not window object

Sur une note rapide, vous ne devriez pas utiliser les fonctions fléchées comme méthodes car elles ne sont pas adaptées à cette fin. Ce n’était qu’à des fins de démonstration.

Nous avons couvert amplement de terrain sur ce sujet. Nous avons commencé par découvrir ce this a été. Nous avons examiné les fonctions, la variété des façons dont elles sont appelées et comment elles affectent la façon dont this est déterminé. Nous avons également examiné attentivement les fonctions fléchées et leurs particularités intéressantes.

J’espère que cet article en a fait assez pour démystifier et vous aider à comprendre le this paramètre une fois pour toutes. Merci d’avoir lu!


Également publié ici

CHARGEMENT EN COURS
. . . & commentaires Suite!

Leave a Comment