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é :
- En tant que fonction-
averageJoe()
dans lequel la fonction est invoquée de manière directe - Comme méthode—
averageJoe.talk()
qui lie l’invocation à un objet, permettant la programmation orientée objet - En tant que constructeur—
new AverageJoe()
dans lequel un nouvel objet est créé - Via les méthodes apply ou call de la fonction—
averageJoe.call(someObject)
ouaverageJoe.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 anotherAverageJoe
qui 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 respectifsthis
valeurs, les raisons étaient différentes. PourregularFunc
c’est parce qu’il a été invoqué d’une manière directe, alors que pourarrowFunc
c’était parce que lewindow
l’objet était l’existantthis
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!