Les possibles valeur de 'this' en JavaScript

Les valeurs de 'this' + le problème de 'this' depuis une inner function

image

La valeur de ‘this’ en JavaScript dépend de :

  • la portée dans laquelle se trouve this;
  • de si this se trouve dans un (callback)[https://developer.mozilla.org/fr/docs/Glossaire/Fonctionderappel].

Nous allons construire un nouvel objet pour se rendre compte du problème de this dans un callback.

// problème de this dans un callback
const barman = {
	name: ‘Bob’,
	bottles: [‘biere’, ‘orange’, ‘coca’],
	informIsOpen: function(bottle) {
		console.log(bottle + ‘ a été ouverte’);
	},
	openBottles: function(){
		console.log(this depuis openBottles’, this);
		this.bottles.forEach(function(bottle){
			console.log(this depuis le callback de this.bottles.forEach(), this);
			this.informIsOpen(bottle);
	})
};


// le 1er this, dans le console.log() depuis openBottles, fait référence à l’objet barman.

// le second this, dans le callback de this.bottles.forEach() fait référence à l’objet window. 

barman.openBottles()

// this depuis openBottles
// { name: ‘Bob’, bottles: Array(3), informIsOpen: f, openBottles: f }

// this depuis le callback de this.bottles.forEach()
// Window {.......}

Donc la fonction informIsOpen() appelée dans le forEach() n’existe pas, puisque le this fait référence, dans le callback, à l’objet Window => normalement this fais référence à window quand il est à la racine du script.

Le fait que 'this' depuis une fonction utilisée par un callback prenne la valeur de window est un bug JavaScript, rassure toi, nous allons pouvoir fixer ça.

Solution pour fixer ce problème => arrow function ES6

Rappelons-nous, une arrow function (fonction fléchée en français) () => {}, hérite de la valeur de this de son parent.

Il suffit donc de modifier la fonction que prend en paramètre notre forEach(), en arrow function => this fera ainsi référence à notre objet Barman et non à l’objet Window.

// arrow function
openBottles: function(){
	this.bottles.forEach((bottle) => {
	console.log(this depuis le cb de this.bottles.forEach(), this);
	this.informIsOpen(bottle);
   })
}

barman.openBottles()

// this depuis le cb de this.bottles.forEach(),
// {name: ‘Bob’, bottles: Array, informIsOpen: f, openBottles: f }
// bière a été ouverte
// orange a été ouverte
// coca a été ouverte

À noter que dans le forEach(), puisqu’il n’y a qu’un seul argument les parenthèses autour de bottle sont optionnelles.

Définir ce que doit valoir this à l’aide de call()

Parfois, ce sera à nous de définir ce que vaut this à l’aide de call().

Dans cet exemple, nous allons définit la valeur de this, pour qu’elle fasse référence à notre user dans notre fonction register().

// préparation à l'utilisation de call()
const user1 = {
	civility: 'M',
	name: {
		first: 'Peter',
		last: 'Parker',
	}
};

const user2 = {
	civility: 'Mme',
	name: {
		first: 'Elektra',
		last: 'Natchios',
	}	
};

const course1 = {
	title: 'lutter contre la peur',
	duration: '1 semaine'
};

const course2 = {
	title: 'apprendre à pardonner',
	duration: '2 semaine'
}; 

function register(course) {
	const message = `${this.civility} ${this.name.last} vous êtes bien enregistré${this.civility === 'Mme' ? 'e' : ''} au cours de ${course.title}`;
	console.log(message);
};  

Pour le moment this {civility et name.last} vaut undefined, car il ne sait pas à quel objet se référer => nous allons donc utiliser le call() pour que la valeur de this soit, user1 ou user2.

call(): le premier argument est l’objet qui sera défini comme contexte de this à l’intérieur de notre fonction register(). Le second argument est l’argument de la fonction sur laquelle le call() est appelée. Jette un oeil au gist au-dessus, tu verras que notre fonction register() prend en argument ‘course’. C’est cet argument qu’on passe directement dans le call().

// call() en action
register.call(user1, course1);

// M Parker vous êtes bien enregistré au cour de lutter contre la peur
  • Sans le call(), M Parker serait undefined.
  • Toutes les fonctions disposent de la fonction call() dans leur prototype.

Définir ce que doit valoir this à l’aide de bind()

Au lieu de recourir à call(), il nous est possible de définir le contexte d’une fonction à l’avance, et cela se fait à l’aide de bind() qui fait aussi partie du prototype des fonctions.

Nous allons créer une fonction qui retourne une fonction grâce au bind() et this aura comme contexte l’user en question.

// bind pour définir le contexte de this à l'avance
const registerParker = register.bind(user1);

registerParker(course1);
// M Parker vous êtes bien inscrit au cours de lutter contre la peur 

Si nous faisons un console.log() de registerParker on peut s’apercevoir que c’est une fonction => c’est donc une fonction qui retourne une fonction, nous sommes ici sur une logique de programmation fonctionnelle.


C’est tout pour aujourd’hui, j’espère que cet article te permettra d’y voir un peu plus clair concernant ce sujet. Je ne prétends pas maîtriser cette question, j’essaie juste de la comprendre un peu plus chaque jour, et écrire à son propos m’aide beaucoup. Je compte sur ton indulgence !

Dans le prochain article, nous ferons le point sur les tableaux en JavaScript.

Latest Blogposts

Hoisting en JavaScript

Voici une notion qui peut être un peu déroutante au début, le hissage en français.

3 January 2021

ARRAY JavaScript

On aborde aujourd'hui le thème des tableaux qui sont eux aussi un élément incontournable de la programmation informatique.

23 January 2020