ES6 en détails est une série d’articles décrivant les nouvelles fonctionnalités ajoutées au langage de programmation JavaScript avec la sixième édition du standard ECMAScript (ES6 en abrégé).

L’article d’aujourd’hui concerne deux fonctionnalités qui rendent les fonctions JavaScript plus expressives : les paramètres du reste et les paramètres par défaut.

Les paramètres du reste (rest parameters)

Lorsqu’on crée une API, on a souvent besoin d’une fonction variadique, c’est-à-dire une fonction qui prend en entrée un nombre variable d’arguments. La méthode String.prototype.concat, par exemple, accepte autant de chaînes qu’on veut. Grâce aux paramètres du reste, ES6 fournit une nouvelle façon d’écrire des fonctions variadiques.

Afin d’illustrer ceci, écrivons une fonction variadique simple, contientTout, qui vérifie si une chaîne contient plusieurs sous-chaînes. On aura par exemple contientTout("banane","b","nan") qui renverra true et contientTout("banane","c","nan") qui renverra false.

Habituellement, on pourrait implémenter cette fonction de cette façon :

function contientTout(tasFoin) {
  for (var i = 1; i < arguments.length; i++) {
    var aiguille = arguments[i];
    if (tasFoin.indexOf(aiguille) === -1) {
      return false;
    }
  }
  return true;
}

Cette implémentation utilise l’objet magique arguments qui est un objet semblable à un tableau et qui contient les paramètres passés à la fonction. Ce code fait ce que l’on souhaite mais sa lisibilité n’est pas optimale. La liste des paramètres de la fonction contient un seul élément, tasFoin : il est donc impossible de voir, d’un simple coup d’œil, que la fonction accepte en réalité plusieurs arguments.

Par ailleurs, il faut être prudent et bien débuter l’itération des arguments à 1 et non à 0. En effet, arguments[0] correspond à tasFoin. Si jamais nous voulons ajouter un autre paramètre avant ou après tasFoin, nous devons nous souvenir qu’il faut mettre à jour la boucle for. Les paramètres du reste permettent de résoudre ces deux problèmes.

Voici une deuxième implémentation contientTout qui utilise les fonctionnalités ES6 et les paramètres du reste :

function contientTout(tasFoin, ...aiguilles) {
  for (var aiguille of aiguilles) {
    if (tasFoin.indexOf(aiguille) === -1) {
      return false;
    }
  }
  return true;
}

Cette version possède le même comportement que la première. Cependant, elle utilise ici la syntaxe spéciale ...aiguilles. Voyons comment l’appel de cette fonction se passe avec contientTout("banane","b","nan"). L’argument tasFoin correspond bien au premier argument, ici "banane". Les points de suspension avant aiguilles indiquent qu’il s’agit des paramètres du reste. Tous les autres paramètres fournis à la fonction sont placés dans un tableau et affectés à la variable aiguilles. Dans notre exemple, aiguilles contient ["b","nan"]. L’exécution de la fonction continue ensuite normalement.

Note : nous avons ici utilisé la boucle ES6 for-of.

Seul le dernier paramètre d’une fonction peut représenter les paramètres du reste. Dans un appel, les paramètres situés avant celui-ci seront traités de façon habituelle. N’importe quel argument additionnel sera mis dans un tableau et ajouté aux paramètres du reste. S’il n’y en a pas, le paramètre du reste sera un tableau vide, il ne sera jamais undefined.

Les paramètres par défaut

Très souvent, il n’est pas nécessaire de passer l’ensemble des paramètres d’une fonction lors d’un appel et on peut avoir des valeurs par défauts raisonnables utilisées quand des paramètres ne sont pas passés. Auparavant, JavaScript a toujours été rigide sur les valeurs par défaut des paramètres : ceux qui n’étaient pas passés à la fonction lors de l’appel valaient inconditionnellement undefined. ES6 introduit une nouvelle syntaxe pour définir des valeurs par défaut personnalisées.

Voici un exemple :

function phraseAnimaux(animaux2="tigres", animaux3="ours") {
    return `Oh ! Des lions, des ${animaux2} et des ${animaux3} !`;
}

Note : cet exemple utilise les gabarits de chaînes de caractères dont on a discuté la semaine dernière.

Pour chaque paramètre, la partie située après le = est une expression qui définit la valeur par défaut à utiliser lorsque l’appelant ne passe pas de valeur pour ce paramètre. Ainsi, phraseAnimaux() renvoie “Oh ! Des lions, des tigres et des ours !”, phraseAnimaux("éléphants") renverra “Oh ! Des lions, des éléphants et des ours  !”, phraseAnimaux("éléphants", "baleines") renverra “Oh ! Des lions, des éléphants et des baleines !”.

Voici quelques subtilités liées aux paramètres par défaut :

  • À la différence de Python, les expressions des valeurs par défaut sont évaluées lors de l’appel de la fonction, de gauche à droite. Cela signifie également que les expressions peuvent utiliser les valeurs calculées pour les paramètres précédents. Par exemple, on pourrait améliorer la fonction précédente sur les phrases avec les animaux de la façon suivante :

    function joliePhraseAnimaux(animaux2="tigres",
      animaux3 = animaux2 == "ours") ? "phoques" : "ours"){
    
      return `Oh ! Des lions, des ${animaux2} et des ${animaux3} !`;
    }
    

    Ainsi, joliePhraseAnimaux("ours") renverrait “Oh ! Des lions, des ours et des phoques !”.

  • Si on passe la valeur undefined, cela sera équivalent à ne rien passer du tout. Ainsi, phraseAnimaux(undefined, "licornes") renvoie “Oh ! Des lions, des tigres et des licornes !”.

  • Un paramètre sans valeur par défaut vaudra undefined s’il n’est pas passé à la fonction lors de l’appel, on a donc :

    function maFonction(a=42, b) {...}

    qui est autorisé et qui est équivalent à

    function maFonction(a=42, b=undefined) {...}

En finir avec l’objet arguments

On a vu ici que les paramètres du reste et les paramètres par défaut permettent de remplacer l’objet arguments. Retirer arguments rend généralement le code plus lisible. En plus de porter atteinte à la lisibilité, le comportement de l’objet arguments pose de nombreux problèmes dès lors qu’on souhaite optimiser les machines virtuelles JavaScript.

On espère que les paramètres du reste et les paramètres par défaut remplaceront complètement arguments. Pour commencer, l’objet arguments ne peut plus être utilisé lorsqu’une fonction utilise des paramètres du reste ou des paramètres par défaut. Malgré cela, le support pour cet objet ne s’arrêtera pas du jour au lendemain (s’il s’arrête un jour). Cependant, il est préférable d’éviter l’objet arguments et d’utiliser les paramètres par défaut et les paramètres du reste dès que possible.

Quand puis-je commencer à utiliser cette fonctionnalité ?

Firefox supporte les paramètres du reste et les valeurs par défaut depuis la version 15.

Malheureusement, aucun autre navigateur ne supporte les paramètres du reste ni les valeurs par défaut pour le moment. V8 a ajouté récemment le support expérimental des paramètres du reste et il existe une issue V8 concernant l’implémentation des valeurs par défaut. De même, JSC a ouvert deux bogues.

Les compilateurs Babel et Traceur supportent tous les deux les valeurs par défaut, il est donc possible de les utiliser dès maintenant.

Conclusion

Bien qu’ils n’ajoutent pas de nouveau comportement d’un point de vue technique, les paramètres du reste et les valeurs par défaut améliorent la lisibilité du code et rendent les déclarations de fonctions JavaScript plus expressives.


Note : Merci à Benjamin Peterson pour avoir implémenté ces éléments dans Firefox, pour ses contributions au projet et bien sûr pour l’article de cette semaine.

La semaine prochaine, nous présenterons un autre élément de ES6 simple, élégant, pratique. Il reprend la syntaxe que vous connaissez déjà pour écrire les tableaux et les objets et la retourne afin de démonter les tableaux et les objets de façon lapidaire. Qu’est-ce que ça veut dire ? Pourquoi voudrait-on démonter un objet ?

Rejoignez-nous la semaine prochaine pour répondre à cette question : Nick Fitzgerald, ingénieur chez Mozilla, nous présentera en détails comment ES6 permet de décomposer des objets.