Jetez un œil aux extraits de code suivants qui résolvent le même problème et réfléchissez à celui que vous préférez.
Voici le premier: | Voici le deuxième: |
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] .filter(int => isEven(int)) .filter(int => isBiggerThan(3, int)) .map(int => int + 1) .map(int => toChar(int)) .filter(char => !isVowel(char)) .join('') // 'fhjl'
| [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] .filter(isEven) .filter(isBiggerThan(3)) .map(plus(1)) .map(toChar) .filter(not(isVowel)) .join('') |
«Je parie que la deuxième option est bien meilleure lisibilité que la première», explique l'auteur du document, dont nous publions aujourd'hui la traduction. Selon lui, tout est dans les arguments des méthodes
filter()
et
map()
.

Aujourd'hui, nous allons parler de la façon de recycler le code similaire au premier exemple afin qu'il ressemble au code du second. L'auteur de l'article promet qu'après avoir compris comment cela fonctionne, vous vous relierez à vos programmes d'une manière nouvelle et vous ne pourrez pas ignorer ce qui semblait tout à fait normal et ne nécessitait pas d'amélioration.
Fonction simple
Considérons une simple fonction
sum()
qui ajoute les nombres qui lui sont passés:
const sum = (a, b) => a + b sum(1, 2)
Nous le
csum()
donnant à la nouvelle fonction le nom
csum()
:
const csum = a => b => a + b csum(1)(2)
Sa nouvelle version fonctionne exactement de la même manière que l'original, la seule différence est la façon dont cette nouvelle fonction est appelée. A savoir, la fonction
sum()
prend deux paramètres à la fois, et
csum()
prend les mêmes paramètres un par un. En fait, lors de l'appel à
csum()
, deux fonctions sont appelées. En particulier, considérez la situation lorsque
csum()
appelée, en lui passant le numéro 1 et rien d'autre:
csum(1)
Un tel appel à
csum()
conduit au fait qu'il renvoie une fonction qui peut accepter le deuxième argument numérique passé à
csum()
lors de son appel habituel, et renvoie le résultat de l'ajout d'un à cet argument. Appelez cette fonction
plusOne()
:
const plusOne = csum(1) plusOne(2)
Travailler avec des tableaux
En JavaScript, vous pouvez travailler avec des tableaux en utilisant une variété de méthodes spéciales. Disons que la méthode
map()
est utilisée pour appliquer la fonction qui lui est passée à chaque élément du tableau.
Par exemple, pour augmenter de 1 chaque élément d'un tableau entier (plus précisément, pour former un nouveau tableau contenant des éléments du tableau d'origine augmentés de 1), vous pouvez utiliser la construction suivante:
[1, 2, 3].map(x => x + 1)
En d'autres termes, ce qui se passe peut être décrit comme suit: la fonction
x => x + 1
prend un entier et retourne le nombre qui le suit dans une série d'entiers. En utilisant la fonction
plusOne()
décrite ci-dessus, cet exemple peut être réécrit comme suit:
[1, 2, 3].map(x => plusOne(x))
Ici, il vaut la peine de ralentir et de réfléchir à ce qui se passe. Si vous faites cela, vous pouvez voir que dans le cas considéré, les constructions
x => plusOne(x)
et
plusOne
(notez que dans cette situation il n'y a pas de crochets après le nom de la fonction) sont équivalentes. Afin de mieux comprendre cela, considérez la fonction
otherPlusOne()
:
const otherPlusOne = x => plusOne(x) otherPlusOne(1)
Le résultat de cette fonction sera le même que celui obtenu par un simple appel à
plusOne()
déjà connu de nous:
plusOne(1)
Pour la même raison, nous pouvons parler de l'équivalence des deux constructions suivantes. Voici le premier que nous avons déjà vu:
[1, 2, 3].map(x => plusOne(x))
Voici le deuxième:
[1, 2, 3].map(plusOne)
De plus, rappelez-vous comment la fonction
plusOne()
été créée:
const plusOne = csum(1)
Cela nous permet de réécrire notre construction avec
map()
comme suit:
[1, 2, 3].map(csum(1))
Maintenant, en utilisant la même technique,
isBiggerThan()
. Si vous le souhaitez, essayez de le faire vous-même, puis lisez la suite. Cela éliminera l'utilisation de constructions inutiles lors de l'utilisation de la méthode
filter()
. Tout d'abord, apportons le code à ce formulaire:
const isBiggerThan = (threshold, int) => int > threshold [1, 2, 3, 4].filter(int => isBiggerThan(3, int))
Ensuite, en nous débarrassant de tout ce qui est superflu, nous obtenons le code que vous avez déjà vu au tout début de ce matériel:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] .filter(isEven) .filter(isBiggerThan(3)) .map(plus(1)) .map(toChar) .filter(not(isVowel)) .join('')
Nous considérons maintenant deux règles simples qui vous permettent d'écrire du code dans le style discuté ici.
Règle numéro 1
Les deux constructions suivantes sont équivalentes:
[…].map(x => fnc(x)) […].map(fnc)
Règle numéro 2
Les rappels peuvent toujours être réécrits pour réduire le nombre d'arguments utilisés pour l'appeler:
const fnc = (x, y, z) => … […].map(x => fnc(x, y, z)) const fnc = (y, z) => x => … […].map(fnc(y, z))
Si vous avez vous-même écrit la fonction
isBiggerThan()
, vous avez probablement déjà eu recours à une telle transformation. Supposons que nous ayons besoin de nombres supérieurs à 3 pour passer à travers un filtre.
const isBiggerThan = (threshold, int) => int > threshold […].filter(int => isBiggerThan(3, int))
Maintenant, nous
isBiggerThan()
fonction
isBiggerThan()
afin qu'elle puisse être utilisée dans la méthode
filter()
et ne pas utiliser la construction
int=>
:
const isBiggerThan = threshold => int => int > threshold […].map(isBiggerThan(3))
Exercice
Supposons que nous ayons le fragment de code suivant:
const keepGreatestChar = (char1, char2) => char1 > char2 ? char1 : char2 keepGreatestChar('b', 'f') // 'f' // 'f' 'b'
Maintenant, basé sur la fonction
keepGreatestChar()
, créez la fonction
keepGreatestCharBetweenBAnd()
. Nous avons besoin que, en l'appelant, nous ne puissions lui passer qu'un seul argument, alors qu'il comparera le caractère qui lui est passé avec le caractère
b
. Cette fonction peut ressembler à ceci:
const keepGreatestChar = (char1, char2) => char1 > char2 ? char1 : char2 const keepGreatestCharBetweenBAnd = char => keepGreatestChar('b', char) keepGreatestCharBetweenBAnd('a')
Maintenant, écrivez la
greatestCharInArray()
fonctionCharInArray
greatestCharInArray()
qui, en utilisant la fonction
keepGreatestChar()
dans la méthode de tableau
keepGreatestChar()
, vous permet de rechercher le "plus grand" caractère et n'a pas besoin d'arguments. Commençons par ce code:
const keepGreatestChar = (char1, char2) => char1 > char2 ? char1 : char2 const greatestCharInArray = array => array.reduce((acc, char) => acc > char ? acc : char, 'a') greatestCharInArray(['a', 'b', 'c', 'd'])
Pour résoudre ce problème, implémentez la fonction
creduce()
, qui peut être utilisée dans la
greatestCharInArray()
fonctionCharInArray
greatestCharInArray()
, qui permet, dans l'application pratique de cette fonction, de ne rien lui passer sauf le tableau dans lequel trouver le caractère avec le plus grand code.
La fonction
creduce()
doit être suffisamment universelle pour pouvoir être utilisée pour résoudre tout problème nécessitant l'utilisation des capacités de la méthode standard de tableau
creduce()
. En d'autres termes, la fonction doit prendre un rappel, une valeur initiale et un tableau pour fonctionner. Par conséquent, vous devriez obtenir une fonction avec laquelle le fragment de code suivant fonctionnera:
const greatestCharInArray = creduce(keepGreatestChar, 'a') greatestCharInArray(['a', 'b', 'c', 'd'])
Résumé
Peut-être que vous vous demandez maintenant pourquoi les méthodes, traitées conformément à la méthodologie présentée ici, ont des noms commençant par le caractère
c
. Le caractère
c
est un acronyme pour curried, et nous avons expliqué comment les fonctions curried aident à améliorer la lisibilité du code. Il convient de noter qu'ici nous ne nous sommes pas efforcés de respecter strictement les principes de la programmation fonctionnelle, mais nous pensons que l'application pratique de ce qui a été discuté ici nous permet d'améliorer le code. Si le sujet du currying en JavaScript est intéressant pour vous - il est recommandé de lire le 4ème chapitre de
ce livre sur la programmation fonctionnelle, et, en général, depuis que vous êtes arrivé à ce point - lisez tout ce livre. De plus, si vous êtes nouveau dans la programmation fonctionnelle, consultez
ce matériel de démarrage.
Chers lecteurs! Utilisez-vous le curry de fonction dans le développement JavaScript?
