Rest parameter et spread operator en Javascript

Comment vous dire que nous nous apprêtons à dédier tout un article aux ... ? Qu’est-ce que le rest parameter et le spread operator ? Bah concrètement, c’est ça : trois petits points.

Et je trouve ça tellement puissant que j’ai réellement écrit un article sur l’intérêt des trois petits points en JavaScript.

Trois petits points

Les trois petits points (...) ne sont pas un opérateur commun en programmation contrairement au = ou au !. C’est ES2015 qui a ajouté cet opérateur à la syntaxe de JavaScript et c’est clairement une bonne chose.

Les trois petits points sont en fait deux choses distinctes, le rest parameter et le spread operator.

Le rest parameter

Ou en français le paramètre du reste sert à stocker une liste indéfinie de valeur sous forme de tableau.

Pourquoi "paramètre" de reste ? Parce que c’est un paramètre de fonction. Il va principalement servir à "ramasser les restes" en paramètre. Par exemple, pour une fonction prenant une liste infinie de paramètres ou pour récupérer les valeurs finales d’un tableau — mais ça nous le verrons dans un prochain article sur la décomposition en JavaScript.

Travaillons donc sur l’exemple de la fonction :

function logArgs(...args) {
   console.log(args)
}

logArgs('coucou', 3, 'Bob') // args == ['coucou', 3, 'Bob']

Voilà l’intérêt de cet opérateur : assembler plusieurs valeurs dans un tableau.

Donc nous avons bien vu pour "c’est un paramètre" mais pour mieux comprendre pourquoi "du reste" voici un autre exemple :

function argsToObject(keys, ...values) {
   let object = {}
   object[keys] = value

   return object
}

let o = argsToObject('fruits', 'pomme', 'poire', 'abricot') 
/* 
 {
   fruits: [
     'pomme',
     'poire',
     'abricot'
   ]
 }
 */ 

Exemple d’une fonction inutile, certes, mais il illustre bien le fait que nous ne sommes pas obligé de mettre tous les arguments dans le paramètre de reste.

Nous pouvons lui dire comme ici : "Je récupère un paramètre qui s’appelle keys et le reste met le dans values"

Pourquoi ne pas utiliser l’objet arguments ?

Premièrement parce qu’il diminue la qualité de code.

En tant que développeurs nous utilisons des fonctions que nous n’avons pas forcément développées nous-même. Si nous nous référons au prototype d’une fonction pour savoir ce qu’elle fait, l’utilisation de l’objet arguments peut nous induire en erreur.

function log()
// une fonction qui s'appelle "log"
// qui ne prend aucun paramètre  

function log(...messages)
// une fonction qui s'appelle "log"
// qui prends autant de messages 
// que je veux en paramètres

Laquelle des deux écritures est plus lisible et plus simple à comprendre ? J’espère que vous avez répondu l’option 2.

Lorsqu’une fonction utilise l’objet arguments nous ne pouvons pas le savoir sans lire le code de cette fonction. Et nous n'avons pas forcément le temps ou l'envie de lire le code de cette fonction.

Ensuite, il n'est pas possible de ne mettre qu'une partie des paramètres dans l'objet arguments. C'est soit tout, soit rien.

Puis, l'objet arguments n'est pas disponible dans les arrow functions, une nouvelle fonctionnalité liée aux fonctions anonymes.

Et enfin, l'utilisation de l'objet arguments est beaucoup plus demandeuse en performances que celle du rest parameter.

Petite précision : lorsque vous utilisez le rest parameter avec une fonction, l'objet arguments n'est plus disponible à l'intérieur de celle-ci.

Le spread operator

Comme nous l'avons vu plus haut, le spread operator — ou opérateur de décomposition — correspond aussi au .... Mais son utilité est complètement opposée à celle du rest parameter.

Le spread operator sert à éclater un tableau en une liste finie de valeurs.

Un exemple vaut mieux qu’un long discours :

let args = ['var 1', 'var 2', 'var 1']

console.log(...args)
// == console.log('var 1', 'var 2', 'var 1')

Le tableau args est éclaté en plusieurs paramètres pour la fonction log de console.

Il est possible que vous n’en voyez pas l’intérêt. Si je me réfère à mes propres cas d’utilisation de l’opérateur avec une fonction, cela peut être utile :

  • Si l’on a besoin de passer exactement la même liste de paramètres à plusieurs fonctions.
  • Si l’on veut automatiser la gestion des méthodes, fonctions, classes. Par exemple, pour l'écriture d'un framework, d'un conteneur d'injection de dépendances.

Autre cas d’utilisation très utile, la concaténation de tableaux.

let fruits = ['pomme', 'poire', 'abricot']
let legumes = ['salade', 'asperge']

// je veux obtenir ['automne', 'hiver', 'pomme', 'poire', 'abricot', 'voiture', 'salade', 'asperge']

Avant, deux solutions s’offrait à nous : boucler des millions de fois pour essayer de faire un algo de concaténation de tableau ou utiliser plusieurs fois la méthode Array.prototype.push.apply().

let fruits = ['pomme', 'poire', 'abricot']
let legumes = ['salade', 'asperge']

let mots = ['automne', 'hiver', ...fruits, 'voiture', ...legumes]

L’opérateur de décomposition simplifie grandement ce type de tâches.

Conclusion

S’il faut expliquer simplement ce qu’est le rest parameter et spread operator en JavaScript :

  • le rest parameter rassemble plusieurs valeurs en un tableau
  • le spread operator découpe un tableau en plusieurs valeurs
  • ils sont tous les deux utilisables grâce aux ...

Pour aller plus loin :