Compréhensions de générateur

Non-standard. Ne pas utiliser !
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.
  • 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)
  • Les compréhensions ES7 peuvent utiliser plusieurs composants if et for.
  • Les compréhensions ES7 ne fonctionnent qu'avec les boucles for...of, pas avec les boucles for...in.

Voir aussi

Étiquettes et contributeurs liés au document

 Contributeurs à cette page : SphinxKnight
 Dernière mise à jour par : SphinxKnight,