Fonctions fléchées en JavaScript : Comment utiliser une syntaxe grasse et concise

Fonctions fléchées en JavaScript : Comment utiliser une syntaxe grasse et concise

Apprenez tout sur les fonctions flèches de JavaScript. Nous vous montrerons comment utiliser la syntaxe des flèches ES6, et certaines des erreurs courantes dont vous devez être conscient lorsque vous utilisez les fonctions flèches dans votre code. Vous verrez de nombreux exemples qui illustrent leur fonctionnement.

Les fonctions flèches JavaScript sont arrivées avec la sortie de l’ECMAScript 2015, également connu sous le nom de ES6. En raison de leur syntaxe concise et de leur gestion du mot-clé this, les fonctions flèches sont rapidement devenues une des fonctions préférées des développeurs.

Syntaxe des fonctions fléchées : Réécriture d’une fonction régulière

Les fonctions sont comme des recettes où vous stockez des instructions utiles pour accomplir quelque chose qui doit se produire dans votre programme, comme exécuter une action ou renvoyer une valeur. En appelant votre fonction, vous exécutez les étapes incluses dans votre recette. Vous pouvez le faire chaque fois que vous appelez cette fonction sans avoir besoin de réécrire la recette encore et encore.

Voici une façon standard de déclarer une fonction puis de l’appeler en JavaScript :

// déclaration de fonction
function sayHiStranger() {
  return 'Hi, stranger !
}

// appel de la fonction
sayHiStranger()

Vous pouvez également écrire la même fonction comme une expression de fonction, comme ceci :

const sayHiStranger = fonction () {
  return 'Hi, stranger !
}

Les fonctions flèches JavaScript sont toujours des expressions. Voici comment vous pourriez réécrire la fonction ci-dessus en utilisant la notation flèche grasse :

const sayHiStranger = () => 'Hi, stranger' (Bonjour, étranger)

Les avantages de cette notation sont les suivants :

  • juste une ligne de code
  • pas de mot-clé function
  • pas de mot-clé return
  • et pas d’accolades {}

En JavaScript, les fonctions sont des « citoyens de première classe » Vous pouvez stocker des fonctions dans des variables, les transmettre à d’autres fonctions en tant qu’arguments, et les renvoyer d’autres fonctions en tant que valeurs. Vous pouvez faire tout cela en utilisant les fonctions flèches JavaScript.

La syntaxe sans parens

Dans l’exemple ci-dessus, la fonction n’a pas de paramètres. Dans ce cas, vous devez ajouter un ensemble de parenthèses vides () avant le symbole de la grosse flèche(=>). Il en va de même lorsque vous avez plus d’un paramètre :

const getNetflixSeries = (seriesName, releaseDate) => `La série ${seriesName} est sortie en ${releaseDate}`
// appelez la fonction
console.log(getNetflixSeries('Bridgerton', '2020') )
// sortie : La série Bridgerton est sortie en 2020

Avec un seul paramètre, cependant, vous pouvez aller de l’avant et laisser de côté les parenthèses (vous n’êtes pas obligé, mais vous pouvez) :

const favoriteSeries = seriesName => seriesName === "Bridgerton" ? "Regardons-la" : "Sortons"
// appelez la fonction
console.log(favoriteSeries("Bridgerton"))
// sortie : "Regardons-le"

Soyez toutefois prudent. Si, par exemple, vous décidez d’utiliser un paramètre par défaut, vous devez l’entourer de parenthèses :

// avec les parenthèses : correct
const bestNetflixSeries = (seriesName = "Bridgerton") => `${seriesName} is the best`
// sort : "Bridgerton est la meilleure"
console.log(bestNetflixSeries())

// pas de parenthèses : erreur
const bestNetflixSeries = seriesName = "Bridgerton" => `${seriesName} is the best`
// Uncaught SyntaxError : invalid arrow-function arguments (les parenthèses autour de la fonction flèche peuvent aider)

Et ce n’est pas parce que vous pouvez, que vous devez. Avec un peu de sarcasme léger et bien intentionné, Kyle Simpson (de You Don’t Know JS fame) a rassemblé ses réflexions sur l’omission des parenthèses dans cet organigramme.

Retour implicite

Lorsque vous n’avez qu’une seule expression dans le corps de votre fonction, vous pouvez rendre la syntaxe des flèches ES6 encore plus concise. Vous pouvez tout garder sur une seule ligne, supprimer les accolades et vous passer du mot-clé return.

Vous venez de voir comment ces jolies lignes uniques fonctionnent dans les exemples ci-dessus. Voici un autre exemple, juste pour faire bonne mesure. La fonction orderByLikes() fait ce qui est écrit sur la boîte : elle renvoie un tableau d’objets de séries Netflix classés par le plus grand nombre d’appréciations :

// en utilisant la fonction JS sort() pour trier les titres par ordre décroissant 
// en fonction du nombre de likes (plus de likes en haut, moins en bas)
const orderByLikes = netflixSeries.sort( (a, b) => b.likes - a.likes )

// appelez la fonction 
// sortie : les titres et le n. de likes par ordre décroissant
console.log(orderByLikes)

C’est cool, mais gardez un œil sur la lisibilité de votre code – surtout lorsque vous enchaînez un tas de fonctions fléchées en utilisant des lignes simples et la syntaxe fléchée ES6 sans parenthèses, comme dans cet exemple :

const greeter = greeting => name => `${greeting}, ${name}!``

Qu’est-ce qui se passe ici ? Essayez d’utiliser la syntaxe de fonction régulière :

function greeter(greeting) {
  return function(name) {
    return `${greeting}, ${name}!` 
  }
} 

Maintenant, vous pouvez rapidement voir comment la fonction externe greeter a un paramètre, greeting, et renvoie une fonction anonyme. Cette fonction interne possède à son tour un paramètre appelé name et renvoie une chaîne de caractères en utilisant la valeur de greeting et de name. Voici comment vous pouvez appeler la fonction :

const myGreet = greeter('Bonjour')
console.log( myGreet('Mary') )   

// sortie 
"Bonjour, Marie !" 

Faites attention à ces erreurs de retour implicite

Lorsque votre fonction flèche JavaScript contient plus d’une déclaration, vous devez toutes les entourer d’accolades et utiliser le mot-clé return.

Dans le code ci-dessous, la fonction construit un objet contenant le titre et le résumé de quelques séries Netflix (les critiques Netflix proviennent du site Rotten Tomatoes) :

const seriesList = netflixSeries.map( série => {
  const container = {}
  container.title = series.name 
  container.summary = series.summary

  // retour explicite
  return container
} )

La fonction flèche à l’intérieur de la fonction .map() se développe sur une série d’instructions, à la fin desquelles elle renvoie un objet. Cela rend inévitable l’utilisation d’accolades autour du corps de la fonction.

De plus, comme vous utilisez des accolades, un retour implicite n’est pas une option. Vous devez utiliser le mot-clé return.

Si votre fonction renvoie un objet littéral en utilisant le retour implicite, vous devez envelopper l’objet à l’intérieur de parenthèses rondes. Si vous ne le faites pas, vous obtiendrez une erreur, car le moteur JavaScript interprète par erreur les accolades de l’objet littéral comme les accolades de la fonction. Et comme vous venez de le remarquer ci-dessus, lorsque vous utilisez des accolades dans une fonction flèche, vous ne pouvez pas omettre le mot-clé return.

La version abrégée du code précédent démontre cette syntaxe :

// Uncaught SyntaxError : unexpected token : ':' (erreur de syntaxe inattendue)
const seriesList = netflixSeries.map(series => {titre : series.name }) ;

// Fonctionne bien
const seriesList = netflixSeries.map(series => ({ title : series.name })) ;

Vous ne pouvez pas nommer les fonctions fléchées

Les fonctions qui n’ont pas d’identifiant de nom entre le mot-clé function et la liste de paramètres sont appelées fonctions anonymes. Voici à quoi ressemble une expression régulière de fonction anonyme :

const anonymous = function() {
  return 'Vous ne pouvez pas m'identifier ! 
}

Les fonctions fléchées sont toutes des fonctions anonymes:

const anonymousArrowFunc = () => 'Vous ne pouvez pas m'identifier ! 

À partir de ES6, les variables et les méthodes peuvent déduire le nom d’une fonction anonyme à partir de sa position syntaxique, en utilisant sa propriété name. Il est ainsi possible d’identifier la fonction lors de l’inspection de sa valeur ou du signalement d’une erreur.

Vérifiez-le en utilisant anonymousArrowFunc:

console.log(anonymousArrowFunc.name)
// sortie : "anonymousArrowFunc"

Sachez que cette propriété de nom inféré n’existe que lorsque la fonction anonyme est affectée à une variable, comme dans les exemples ci-dessus. Si vous utilisez une fonction anonyme comme callback, vous perdez cette fonctionnalité utile. Ceci est illustré dans la démo ci-dessous où la fonction anonyme dans la méthode .setInterval() ne peut pas se prévaloir de la propriété de nom:

let counter = 5
let countDown = setInterval(() => {
  console.log(counter)
  counter--
  if (counter === 0) {
    console.log("Je n'ai pas de nom !")
    clearInterval(countDown)
  }
}, 1000)

Et ce n’est pas tout. Cette propriété de nom déduite ne fonctionne toujours pas comme un identifiant approprié que vous pouvez utiliser pour faire référence à la fonction depuis l’intérieur de celle-ci – comme pour la récursion, les événements de déliaison, etc.

L’anonymat intrinsèque des fonctions flèches a conduit Kyle Simpson à exprimer son point de vue sur celles-ci comme suit :

Comme je ne pense pas que les fonctions anonymes soient une bonne idée à utiliser fréquemment dans vos programmes, je ne suis pas un fan de l’utilisation de la forme => fonction flèche. – Vous ne connaissez pas JS

Comment les fonctions fléchées traitent le mot-clé this

La chose la plus importante à retenir au sujet des fonctions flèches est la façon dont elles traitent le mot-clé this. En particulier, le mot-clé this à l’intérieur d’une fonction flèche n’est pas rebondi.

Pour illustrer ce que cela signifie, regardez la démo ci-dessous :

[codepen_embed height= »300″ default_tab= »html,result » slug_hash= »qBqgBmR » user= »SitePoint »]Voir le stylo
JS this dans les fonctions flèches par SitePoint (@SitePoint)
sur CodePen.[/codepen_embed]

Voici un bouton. Le fait de cliquer sur le bouton déclenche un compteur inversé de 5 à 1, qui s’affiche sur le bouton lui-même.



...

const startBtn = document.querySelector(".start-btn") ;

startBtn.addEventListener('click', function() {
  this.classList.add('counting')
  let counter = 5 ;
  const timer = setInterval(() => {
    this.textContent = counter 
    compteur -- 
    if(counter 

Remarquez que le gestionnaire d’événement dans la méthode .addEventListener() est une expression de fonction anonyme régulière, et non une fonction fléchée. Pourquoi ? Si vous l' enregistrez à l’intérieur de la fonction, vous verrez qu’elle fait référence à l’élément bouton auquel l’écouteur a été attaché, ce qui est exactement ce qui est attendu et ce qui est nécessaire pour que le programme fonctionne comme prévu :

startBtn.addEventListener('click', function() {
  console.log(this)
  ...
})

Voici à quoi cela ressemble dans la console des outils de développement de Firefox :

log of the this keyword inside the regular anonymous function expression that handles the click event for the button

Essayez toutefois de remplacer la fonction régulière par une fonction flèche, comme ceci :

startBtn.addEventListener('click', () => {
  console.log(this)
  ...
})

Maintenant, ceci ne fait plus référence au bouton. Au lieu de cela, elle fait référence à l’objet Window:

window object referenced by the this keyword inside the JavaScript arrow function passed to the event listener

Cela signifie que, si vous voulez l' utiliser pour ajouter une classe au bouton après qu’il ait été cliqué, par exemple, votre code ne fonctionnera pas :

// modifiez l'apparence de la bordure du bouton
this.classList.add('comptage')

Voici le message d’erreur dans la console :

this.classList is undefined

Lorsque vous utilisez une fonction flèche en JavaScript, la valeur du mot-clé this n’est pas rebondie. Elle est héritée de la portée parent (c’est ce que l’on appelle lexical scoping). Dans ce cas particulier, la fonction flèche en question est transmise comme argument à la méthode startBtn.addEventListener(), qui se trouve dans la portée globale. Par conséquent, l’élément this à l’intérieur du gestionnaire de fonction est également lié à la portée globale – c’est-à-dire à l’objet Window.

Donc, si vous voulez que this fasse référence au bouton de démarrage dans le programme, l’approche correcte consiste à utiliser une fonction ordinaire, et non une fonction flèche.

Fonctions fléchées anonymes

La prochaine chose à remarquer dans la démo ci-dessus est le code à l’intérieur de la méthode .setInterval(). Ici aussi, vous trouverez une fonction anonyme, mais cette fois, il s’agit d’une fonction flèche. Pourquoi ?

Remarquez ce que serait la valeur de cette méthode si vous utilisiez une fonction ordinaire :

const timer = setInterval(function() {
  console.log(this)
  ...
}, 1000)

Est-ce que ce serait l’élément bouton? Pas du tout. Ce serait l’objet Window!

Using a regular function inside setInterval() changes the reference of the this keyword from the button to the Window object

En fait, le contexte a changé, puisque this se trouve maintenant à l’intérieur d’une fonction non liée ou globale qui est transmise comme argument à .setInterval(). Par conséquent, la valeur du mot-clé this a également changé, car il est maintenant lié à la portée globale.

Une astuce courante dans cette situation consiste à inclure une autre variable pour stocker la valeur du mot-clé this afin qu’il continue à faire référence à l’élément attendu – dans ce cas, l’élément bouton:

const that = this
const timer = setInterval(function() {
  console.log(that)
  ...
}, 1000)

Vous pouvez également utiliser .bind() pour résoudre le problème :

const timer = setInterval(function() {
  console.log(this)
  ...
}.bind(this), 1000)

Avec les fonctions flèches, le problème disparaît complètement. Voici ce que cela donne lorsque vous utilisez une fonction flèche :

const timer = setInterval( () => { 
  console.log(this)
  ...
}, 1000)
the value of this inside the arrow function passed to setInterval()

Cette fois, la console enregistre le bouton, ce qui est ce que nous voulons. En fait, le programme va modifier le texte du bouton, il a donc besoin de ceci pour faire référence à l’élément bouton:

const timer = setInterval( () => { 
  console.log(this)
 // le texte du bouton affiche la valeur du timer
  this.textContent = compteur
}, 1000)

Les fonctions flèches n’ont pas leur propre contexte this. Elles héritent de la valeur de this du parent, et c’est grâce à cette caractéristique qu’elles constituent un excellent choix dans des situations comme celle ci-dessus.

Les fonctions flèches ne sont pas seulement une nouvelle façon d’écrire des fonctions en JavaScript. Elles ont leurs propres limites, ce qui signifie qu’il y a des cas où vous ne voulez pas en utiliser une. Le gestionnaire de clic de la démo précédente en est un exemple, mais ce n’est pas le seul. Examinons-en quelques autres.

Fonctions fléchées en tant que méthodes d’objet

Les fonctions fléchées ne fonctionnent pas bien en tant que méthodes sur des objets. Voici un exemple.

Considérons cet objet netflixSeries, qui possède quelques propriétés et quelques méthodes. L’appel de console.log(netflixSeries.getLikes()) doit imprimer un message avec le nombre actuel de likes, et l’appel de console.log(netflixSeries.addLike()) doit augmenter le nombre de likes d’une unité et imprimer la nouvelle valeur avec un message de remerciement sur la console :

const netflixSeries = {
  title : 'After Life', 
  firstRealease : 2019,
  likes : 5,
  getLikes : () => `${this.title} a ${this.likes} likes`,
  addLike : () => { 
    this.likes++
    return `Merci d'avoir aimé ${this.title}, qui a maintenant ${this.likes} likes`
  } 
}

Au lieu de cela, l’appel de la méthode .getLikes() renvoie « undefined has NaN likes », et l’appel de la méthode .addLike() renvoie « Thank you for liking undefined, which now has NaN likes ». Ainsi, this.title et this.likes ne parviennent pas à référencer les propriétés title et likes de l’objet respectivement.

Une fois de plus, le problème se situe au niveau du scoping lexical des fonctions flèches. Le this à l’intérieur de la méthode de l’objet fait référence à la portée du parent, qui dans ce cas est l’objet Window, pas le parent lui-même – c’est-à-dire, pas l’objet netflixSeries.

La solution, bien sûr, est d’utiliser une fonction régulière :

const netflixSeries = {
  title : 'After Life', 
  firstRealease : 2019,
  likes : 5,
  getLikes() {
    return `${this.title} a ${this.likes} likes`
  },
  addLike() { 
    this.likes++
    return `Merci d'avoir aimé ${this.title}, qui a maintenant ${this.likes} likes`
  } 
}

// appelez les méthodes 
console.log(netflixSeries.getLikes())
console.log(netflixSeries.addLike())

// sortie 
After Life a 5 likes
Merci d'avoir aimé After Life, qui a maintenant 6 likes

Fonctions fléchées avec des bibliothèques tierces

Une autre difficulté à laquelle il faut faire attention est que les bibliothèques tierces lient souvent les appels de méthode de sorte que la valeur this pointe vers quelque chose d’utile.

Par exemple, à l’intérieur d’un gestionnaire d’événements jQuery, this vous donnera accès à l’élément DOM auquel le gestionnaire était lié :

$('body').on('click', function() {
  console.log(this)
})
// 

Mais si nous utilisons une fonction flèche – qui, comme nous l’avons vu, ne possède pas son propre contexte this – nous obtenons des résultats inattendus :

$('body').on('click', () =>{
  console.log(this)
})
// Fenêtre

Voici un autre exemple utilisant Vue :

new Vue({
  el : app,
  data : {
    message : 'Hello, World !
  },
  created : function() {
    console.log(this.message) ;
  }
})
// Bonjour, le monde !

Dans le hook créé, celui-ci est lié à l’instance Vue, de sorte que le message « Hello, World ! » s’affiche.

Cependant, si nous utilisons une fonction flèche, celle-ci pointera vers la portée parent, qui ne possède pas de propriété message:

new Vue({
  el : app,
  data : {
    message : 'Hello, World !
  },
  created : function() {
    console.log(this.message) ;
  }
})
// indéfini

Les fonctions flèches n’ont pas d’objet d’arguments

Parfois, vous pouvez avoir besoin de créer une fonction avec un nombre indéfini de paramètres. Par exemple, disons que vous voulez créer une fonction qui liste vos séries Netflix préférées classées par préférence. Cependant, vous ne savez pas encore combien de séries vous allez inclure. JavaScript met à disposition l’objet arguments. Il s’agit d’un objet semblable à un tableau (mais pas un tableau complet) qui stocke les valeurs transmises à la fonction lorsqu’elle est appelée.

Essayez d’implémenter cette fonctionnalité en utilisant une fonction flèche :

const listYourFavNetflixSeries = () => {
  // nous devons transformer les arguments en un véritable tableau 
  // afin que nous puissions utiliser .map()
  const favSeries = Array.from(arguments) 
  return favSeries.map( (série, i) => {
    return `${series} est ma #${i +1} série Netflix préférée`  
  } )
  console.log(arguments)
}

console.log(listYourFavNetflixSeries('Bridgerton', 'Ozark', 'After Life')) 

Lorsque vous appelez la fonction, vous obtenez le message d’erreur suivant : Uncaught ReferenceError : arguments n'est pas défini. Ce que cela signifie, c’est que l’objet arguments n’est pas disponible à l’intérieur des fonctions flèches. En fait, le remplacement de la fonction flèche par une fonction ordinaire fait l’affaire :

const listYourFavNetflixSeries = function() {
   const favSeries = Array.from(arguments) 
   return favSeries.map( (series, i) => {
     return `${series} est ma #${i +1} série Netflix préférée`  
   } )
   console.log(arguments)
 }
console.log(listYourFavNetflixSeries('Bridgerton', 'Ozark', 'After Life'))

// sortie 
["Bridgerton est ma série Netflix préférée n°1", "Ozark est ma série Netflix préférée n°2", "After Life est ma série Netflix préférée n°3"]

Donc, si vous avez besoin de l’objet arguments, vous ne pouvez pas utiliser les fonctions flèches.

Mais que faire si vous voulez vraiment utiliser une fonction flèche pour reproduire la même fonctionnalité ? Une chose que vous pouvez faire est d’utiliser les paramètres de repos ES6 (... ). Voici comment vous pourriez réécrire votre fonction :

const listYourFavNetflixSeries = (...seriesList) => {
   return seriesList.map( (series, i) => {
     return `${series} est ma #${i +1} série Netflix préférée`
   } )
 }

Conclusion

En utilisant les fonctions flèches, vous pouvez écrire des fonctions concises à une ligne avec retour implicite et enfin oublier les vieux trucs pour résoudre la liaison du mot-clé this en JavaScript. Les fonctions flèches fonctionnent également très bien avec les méthodes de tableau comme .map(), .sort(), .forEach(), .filter() et .reduce(). Mais n’oubliez pas : les fonctions flèches ne remplacent pas les fonctions JavaScript ordinaires. N’utilisez les fonctions flèches JavaScript que lorsqu’elles sont l’outil idéal pour le travail à accomplir.

Si vous avez des questions sur les fonctions fléchées ou si vous avez besoin d’aide pour les utiliser correctement, je vous recommande de vous rendre sur les forums conviviaux de SitePoint. Vous y trouverez de nombreux programmeurs compétents prêts à vous aider.

Laurent

Laurent est un développeur web originaire de Corée. Il aime construire des choses pour le web et partager ce qu'il a appris en écrivant sur son blog. Quand il n'est pas en train de coder ou d'apprendre quelque chose de nouveau, il aime regarder des dessins animés et jouer à des jeux vidéo.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *