Comment utiliser les fonctions d’ordre supérieur en JavaScript

Comment utiliser les fonctions d’ordre supérieur en JavaScript

L’une des caractéristiques de JavaScript qui le rend bien adapté à la programmation fonctionnelle est le fait qu’il peut accepter des fonctions d’ordre supérieur. Une fonction d’ordre supérieur est une fonction qui peut prendre une autre fonction comme argument, ou qui renvoie une fonction comme résultat.

Fonctions de première classe : Les fonctions en tant qu’objets en JavaScript

Vous avez peut-être entendu dire que JavaScript traite les fonctions comme des citoyens de première classe. Ce que cela signifie, c’est que les fonctions en JavaScript sont traitées comme des objets. Elles ont le type Object, elles peuvent être assignées comme valeur d’une variable, et elles peuvent être transmises et retournées comme toute autre variable de référence.

Cette capacité native donne à JavaScript des pouvoirs spéciaux lorsqu’il s’agit de programmation fonctionnelle. Comme les fonctions sont des objets, le langage prend en charge une approche très naturelle de la programmation fonctionnelle. En fait, c’est tellement naturel que je parie que vous l’utilisez sans même y penser.

Comment les fonctions d’ordre supérieur prennent des fonctions comme arguments

Si vous avez fait beaucoup de programmation JavaScript sur le Web ou de développement frontal, vous avez probablement rencontré des fonctions qui utilisent un callback. Une fonction de rappel est une fonction qui est exécutée à la fin d’une opération, une fois que toutes les autres opérations ont été effectuées. Habituellement, cette fonction de rappel est passée en tant que dernier argument de la fonction. Souvent, elle est définie en ligne comme une fonction anonyme.

Puisque JavaScript est monofilaire, ce qui signifie qu’une seule opération se produit à la fois, chaque opération qui va se produire est mise en file d’attente le long de ce fil unique. La stratégie consistant à passer une fonction qui sera exécutée après la fin des autres opérations de la fonction parent est l’une des caractéristiques de base des langages qui prennent en charge les fonctions d’ordre supérieur. Elle permet un comportement asynchrone, de sorte qu’un script peut continuer à s’exécuter tout en attendant un résultat. La possibilité de passer une fonction de rappel est essentielle lorsqu’il s’agit de ressources qui peuvent renvoyer un résultat après une période de temps indéterminée.

C’est très utile dans un environnement de programmation Web, où un script peut envoyer une requête Ajax à un serveur, puis devoir traiter la réponse dès qu’elle arrive, sans connaître à l’avance la latence du réseau ou le temps de traitement sur le serveur. Node.js utilise fréquemment les callbacks pour faire l’usage le plus efficace des ressources du serveur. Cette approche est également utile dans le cas d’une application qui attend une entrée utilisateur avant d’exécuter une fonction.

Par exemple, considérez cet extrait de JavaScript simple qui ajoute un écouteur d’événements à un bouton.

Alors Cliquable

document.getElementById("cliquable").addEventListener("click", fonction() {
alerte("vous avez déclenché " + ce.id);
});

Ce script utilise une fonction inline anonyme pour afficher une alerte. Mais il aurait tout aussi bien pu utiliser une fonction définie séparément et transmettre cette fonction nommée à la méthode addEventListener


var proveIt = function() {
alert("vous avez déclenché " + this.id);
};

document.getElementById("clicker").addEventListener("click", proveIt);

Notez que nous avons transmis proveIt et non proveIt() à notre fonction addEventListener. Lorsque vous transmettez une fonction par son nom sans parenthèses, vous transmettez l’objet fonction lui-même. Lorsque vous la transmettez avec des parenthèses, vous transmettez le résultat de l’exécution de cette fonction.

Notre petite fonction proveIt() est structurellement indépendante du code qui l’entoure et renvoie toujours l’identifiant de l’élément déclenché. Ce bout de code pourrait exister dans n’importe quel contexte dans lequel vous souhaitez afficher une alerte avec l’id d’un élément, et pourrait être appelé avec n’importe quel écouteur d’événement.

La possibilité de remplacer une fonction en ligne par une fonction définie et nommée séparément ouvre un monde de possibilités. Alors que nous essayons de développer des fonctions pures qui ne modifient pas les données externes et renvoient le même résultat pour la même entrée à chaque fois, nous disposons maintenant de l’un des outils essentiels pour nous aider à développer une bibliothèque de petites fonctions ciblées qui peuvent être utilisées de manière générique dans n’importe quelle application.

Renvoyer des fonctions comme résultats avec des fonctions d’ordre supérieur

En plus de prendre des fonctions comme arguments, JavaScript permet aux fonctions de retourner d’autres fonctions comme résultat. Cela est parfaitement logique, puisque les fonctions sont simplement des objets, elles peuvent être retournées de la même manière que n’importe quelle autre valeur.

Mais qu’est-ce que cela signifie de retourner une fonction comme résultat ? Définir une fonction comme la valeur de retour d’une autre fonction vous permet de créer des fonctions qui peuvent être utilisées comme modèles pour créer de nouvelles fonctions. Cela ouvre la porte à un autre monde de magie fonctionnelle JavaScript.

Par exemple, imaginez que vous en avez assez de lire tous ces articles sur la spécificité des Millennials, et que vous décidez de remplacer le mot Millennials par l’expression Snake People chaque fois qu’il apparaît. Votre réflexe pourrait être de simplement écrire une fonction qui effectuerait ce remplacement de texte sur tout texte que vous lui passeriez :

var snakify = function(text) {
return text.replace(/millenials/ig, "Gens du serpent");
};
console.log(snakify("Les Millenials préparent toujours quelque chose."));

Cela fonctionne, mais c’est assez spécifique à cette seule situation. Vous êtes également fatigué d’entendre parler des baby-boomers. Vous aimeriez créer une fonction personnalisée pour eux aussi. Mais même avec une fonction aussi simple, vous ne voulez pas avoir à répéter le code que vous avez écrit :

var hippify = function(text) {
return text.replace(/baby-boomers/ig, "Hippies vieillissants");
};
console.log(hippify("Les Baby Boomers regardent simplement ailleurs."));

Mais que se passerait-il si vous décidiez de faire quelque chose de plus sophistiqué pour préserver la casse dans la chaîne originale ? Vous devriez modifier vos deux nouvelles fonctions pour le faire. C’est embêtant, et cela rend votre code plus fragile et plus difficile à lire.

Ce que vous voulez vraiment, c’est la flexibilité de pouvoir remplacer n’importe quel terme par n’importe quel autre terme dans une fonction de modèle, et de définir ce comportement comme une fonction de base à partir de laquelle vous pourriez créer toute une série de fonctions personnalisées.

Avec la possibilité de renvoyer des fonctions au lieu de valeurs, JavaScript offre des moyens de rendre ce scénario beaucoup plus pratique :

var attitude = function(original, replacement,source) {
return function(source) {
return source.replace(original, replacement);
};
};

var snakify = attitude(/milléniaux/ig, "Les gens des serpents");
var hippify = attitude(/baby-boomers/ig, "Hippies vieillissants");

console.log(snakify("Les Millenials préparent toujours quelque chose."));

console.log(hippify("Les Baby Boomers se contentent de regarder ailleurs."));

Ce que nous avons fait, c’est isoler le code qui effectue le travail réel dans une fonction d’attitude polyvalente et extensible qui encapsule tout le travail nécessaire pour modifier toute chaîne d’entrée en utilisant une phrase originale et une phrase de remplacement avec une certaine attitude.

La définition d’une nouvelle fonction comme référence à la fonction d’attitude, préremplie avec les deux premiers arguments qu’elle prend, permet à la nouvelle fonction de prendre n’importe quel argument que vous lui passez et de l’utiliser comme texte source dans la fonction interne renvoyée par la fonction d’attitude.

Nous profitons ici du fait que les fonctions JavaScript ne se soucient pas de savoir si elles reçoivent le même nombre d’arguments que celui pour lequel elles ont été définies à l’origine. Si un argument est manquant, la fonction traitera simplement les arguments manquants comme non définis.

D’autre part, cet argument supplémentaire peut être transmis ultérieurement lorsque la fonction appelée a été définie de la manière que nous venons de démontrer, en tant que référence à une fonction renvoyée par une autre fonction avec un argument (ou plus) laissé indéfini.

Relisez cette démonstration plusieurs fois si nécessaire, afin de bien comprendre ce qui se passe. Nous créons une fonction modèle qui renvoie une autre fonction. Ensuite, nous définissons cette nouvelle fonction retournée, moins un attribut, comme une implémentation personnalisée de la fonction modèle. Toutes les fonctions créées de cette manière hériteront du même code de travail de la fonction modèle, mais pourront être prédéfinies avec différents arguments par défaut.

Vous utilisez déjà des fonctions d’ordre supérieur

Les fonctions d’ordre supérieur sont tellement fondamentales pour le fonctionnement de JavaScript que vous les utilisez déjà. Chaque fois que vous passez une fonction anonyme ou un callback, vous prenez en fait la valeur que la fonction passée renvoie et l’utilisez comme argument pour une autre fonction (comme avec les fonctions flèches).

La capacité des fonctions à renvoyer d’autres fonctions étend la commodité de JavaScript, en nous permettant de créer des fonctions au nom personnalisé pour effectuer des tâches spécialisées avec du code modèle partagé. Chacune de ces petites fonctions peut hériter des améliorations apportées au code d’origine par la suite, ce qui nous permet d’éviter la duplication du code et de garder notre source propre et lisible.

En prime, si vous vous assurez que vos fonctions sont pures, c’est-à-dire qu’elles ne modifient pas les valeurs externes et qu’elles renvoient toujours la même valeur pour toute entrée donnée, vous vous placez dans une bonne position pour créer des suites de tests afin de vérifier que vos modifications de code ne cassent pas les éléments sur lesquels vous vous appuyez lorsque vous mettez à jour vos fonctions de modèle.

Commencez à réfléchir à la manière dont vous pouvez tirer parti de cette approche dans vos propres projets. L’un des grands avantages de JavaScript est que vous pouvez mélanger des techniques fonctionnelles avec le code que vous connaissez déjà. Tentez quelques expériences. Vous serez peut-être surpris de voir à quel point un petit travail avec des fonctions d’ordre supérieur peut facilement améliorer votre code.

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 *