Les compréhensions de générateurs ne sont pas une fonctionnalité standard et il est peu vraisemblable qu'elles soient ajoutées à ECMAScript. Mieux vaut utiliser les générateurs pour des fonctionnalités similaires.
Cette fonctionnalité fait partie des propositions pour Harmony (ECMAScript7) et est donc expérimentale.
Cette spécification technique n'a pas encore été stabilisée, veuillez consulter le tableau de compatibilité pour plus d'informations sur les éventuelles différences entre les navigateurs. Il convient de noter qu'une fonctionnalité expérimentale peut voir sa syntaxe ou son comportement modifié dans le futur, en fonction des évolutions de la spécification.
La syntaxe de compréhension de générateur et une expression qui permet de construire rapidement une fonction génératrice à partir d'un objet itérable. Ces compréhensions existent dans de nombreux langages de programmation.
Voir ci-après pour plus d'informations sur les différences avec l'ancienne syntaxe SpiderMonkey basée sur des propositions pour ECMAScript 4.
Syntaxe
(for (x of itérable) x) (for (x of itérable) if (condition) x) (for (x of itérable) for (y of itérable) x + y)
Description
Une compréhension de générateur peut contenir deux sortes de composants :
L'itération for-of
est toujours le premier composant. Il est possible d'utiliser plusieurs itérations for-of
et plusieurs instructions if
.
Les compréhensions de tableaux ont un inconvénient majeur : quand on les utilise, un nouveau tableau est créé en mémoire. Cela ne pose pas de problème particulier quand le tableau en question est petit (l'impact sera alors léger) mais lorsque le tableau est très grand (voire infini avec un générateur), cela peut poser problème que de vouloir créer un nouveau tableau.
Les générateurs permettent de calculer des suites à la demande (chaque élément successif est calculé lorsqu'on en a besoin). Les compréhensions de générateurs sont presque identiques, d'une point de vue syntaxique, aux compréhensions de tableaux. Plutôt d'utiliser des crochets, elles utilisent des parenthèses et au lieu de créer un tableau, elles créent un générateur qui pourra être utilisé. Cette notation peut être vue comme une notation raccourcie pour créer des générateurs.
Imaginons qu'on ait un itérateur qui parcourt une grande série d'entiers et qu'on veuille créer un itérateur qui itère sur les doubles de ces entiers. Une compréhension de tableau entraînerait la création d'un tableau complet en mémoire, dont les éléments seraient les valeurs doublées :
var doubles = [for (i in it) i * 2];
En revanche, une compréhension de générateur permettrait de créer un nouvel itérateur qui pourrait être utilisé pour créer les valeurs doublées à la demande, quand on a besoin de les utiliser :
var it2 = (for (i in it) i * 2); console.log(it2.next()); // La première valeur, doublée console.log(it2.next()); // La deuxième valeur, doublée
Lorsqu'une compréhension de générateur est utilisée comme un argument d'une fonction, les parenthèses utilisées pour l'appel de la fonction permettent de ne pas écrire les parenthèse encadrant la compréhension :
var résultat = faireQuelqueChose(for (i in it) i * 2);
Avec la compréhension de générateur, on ne parcourt qu'une fois la structure de l'objet alors qu'avec une compréhension de tableau, on parcourt une fois le tableau pour construire la nouvelle version puis une seconde fois quand on souhaite l'utiliser.
Exemples
Compréhensions simples
(for (i of [ 1, 2, 3 ]) i*i ); // fonction génératrice qui générera 1, 4, et 9 [...(for (i of [ 1, 2, 3 ]) i*i )]; // [1, 4, 9] var abc = [ "A", "B", "C" ]; (for (lettres of abc) lettres.toLowerCase()); // fonction génératrice qui générera "a", "b", et "c"
Compréhensions utilisant une instruction if
var années = [ 1954, 1974, 1990, 2006, 2010, 2014 ]; (for (année of années) if (année > 2000) année); // fonction génératrice qui générera 2006, 2010, et 2014 (for (année of années) if (année > 2000) if(année < 2010) année); // fonction génératrice qui générera 2006, équivaut à : (for (année of années) if (année > 2000 && année < 2010) année); // fonction génératrice qui générera 2006
Compréhensions de générateurs et fonctions génératrices
Pour mieux comprendre la syntaxe des compréhensions, on peut la comparer avec celle des fonctions génératrices :
Exemple 1 : Générateur simple.
var nombres = [ 1, 2, 3 ]; // Fonction génératrice (function*() { for (let i of nombres) { yield i * i; } })() // Compréhension de générateur (for (i of nombres) i*i ); // Résultat : les deux instructions renvoient chacune un générateur pour créer [ 1, 4, 9 ]
Second exemple : Un générateur avec if
.
var nombres = [ 1, 2, 3 ]; // Fonction génératrice (function*() { for (let i of nombres) { if (i < 3) { yield i * 1; } } })() // Compréhension (for (i of nombres) if (i < 3) i); // Résultat : les deux renvoient un générateur qui générera [ 1, 2 ]
Spécifications
Était initialement prévu pour le brouillon ECMAScript 6 mais fut retiré lors de la révision 27 (août 2014). Consulter les révisions antérieures d'ES 6 pour les spécifications de cette sémantique.
Compatibilité des navigateurs
Fonctionnalité | Chrome | Firefox (Gecko) | Internet Explorer | Opera | Safari |
---|---|---|---|---|---|
Support simple | Pas de support | 30 (30) | Pas de support | Pas de support | Pas de support |
Fonctionnalité | Android | Chrome pour Android | Firefox Mobile (Gecko) | IE Mobile | Opera Mobile | Safari Mobile |
---|---|---|---|---|---|---|
Support simple | Pas de support | Pas de support | 30.0 (30) | Pas de support | Pas de support | Pas de support |
Notes relatives à l'implémentation de SpiderMonkey
let
n'est pas supporté comme identifiant car il n'est disponible qu'avec JavaScript 1.7 et pour la manipulations des balises de script XUL.- La décomposition, utilisée dans les compréhensions, n'est pas encore supportée (bug 980828).
Différences avec les anciennes compréhensions JS 1.7 et JS 1.8
Les compréhensions « JS1.7 / JS1.8 » ont été retirées à partir de Gecko 46 (bug 1220564).
Ancienne syntaxe pour les compréhensions (ne plus l'utiliser) :
[X for (Y in Z)] [X for each (Y in Z)] [X for (Y of Z)]
Les différences :
- Les compréhensions ES7 créent une portée par nœud
for
et non pas une portée pour l'ensemble de la compréhension.- Ancienne version :
[...(()=>x for (x of [0, 1, 2]))][1]() // 2
- Nouvelle version:
[...(for (x of [0, 1, 2]) ()=>x)][1]() // 1, chaque itération crée une nouvelle liaison pour x.
- Ancienne version :
- Les compréhensions ES7 débutent par
for
et non pas l'expression d'affectation.- Ancienne version :
(i * 2 for (i of nombres))
- Nouvelle version :
(for (i of nombres) i * 2)
- Ancienne version :
- Les compréhensions ES7 peuvent utiliser plusieurs composants
if
etfor
. - Les compréhensions ES7 ne fonctionnent qu'avec les boucles
, pas avec les bouclesfor...of
.for...in