ES6 - Itérateur

Introduction à Iterator

Iterator est un objet qui nous permet d'accéder un à un à une collection d'objets.

Les types intégrés suivants sont par défaut itérables -

  • String
  • Array
  • Map
  • Set

Un objet est considéré iterable, si l'objet implémente une fonction dont la clé est [Symbol.iterator]et renvoie un itérateur. Une boucle for ... of peut être utilisée pour itérer une collection.

Exemple

L'exemple suivant déclare un tableau, le marque et l'itère en utilisant un for..of boucle.

<script>
   let marks = [10,20,30]
   //check iterable using for..of
   for(let m of marks){
      console.log(m);
   }
</script>

La sortie du code ci-dessus sera comme indiqué ci-dessous -

10
20
30

Exemple

L'exemple suivant déclare un tableau, marque et récupère un objet itérateur. le[Symbol.iterator]()peut être utilisé pour récupérer un objet itérateur. La méthode next () de l'itérateur renvoie un objet avec'value' et 'done'Propriétés . 'done' est booléen et renvoie true après avoir lu tous les éléments de la collection.

<script>
   let marks = [10,20,30]
   let iter = marks[Symbol.iterator]();
   console.log(iter.next())
   console.log(iter.next())
   console.log(iter.next())
   console.log(iter.next())
</script>

La sortie du code ci-dessus sera comme indiqué ci-dessous -

{value: 10, done: false}
{value: 20, done: false}
{value: 30, done: false}
{value: undefined, done: true}

Iterable personnalisé

Certains types en JavaScript sont itérables (par exemple, tableau, carte, etc.) tandis que d'autres ne le sont pas (par exemple, classe). Les types JavaScript qui ne sont pas itérables par défaut peuvent être itérés en utilisant le protocole itérable.

L'exemple suivant définit une classe nommée CustomerListqui stocke plusieurs objets client sous forme de tableau. Chaque objet client a des propriétés firstName et lastName.

Pour rendre cette classe itérable, la classe doit implémenter [Symbol.iterator]()fonction. Cette fonction renvoie un objet itérateur. L'objet itérateur a une fonctionnext qui renvoie un objet {value:'customer',done:true/false}.

<script>
   //user defined iterable
   class CustomerList {
      constructor(customers){
         //adding customer objects to an array
         this.customers = [].concat(customers)
      }
      //implement iterator function
      [Symbol.iterator](){
         let count=0;
         let customers = this.customers
         return {
            next:function(){
            //retrieving a customer object from the array
               let customerVal = customers[count];
               count+=1;
               if(count<=customers.length){
                  return {
                     value:customerVal,
                     done:false
                  }
               }
               //return true if all customer objects are iterated
               return {done:true}
            }
         }
      }
   }
   //create customer objects
   let c1={
      firstName:'Sachin',
      lastName:'Tendulkar'
   }
   let c2={
      firstName:'Rahul',
      lastName:'Dravid'
   }
   //define a customer array and initialize it let customers=[c1,c2]
   //pass customers to the class' constructor
   let customersObj = new CustomerList(customers);
   //iterating using for..of
   for(let c of customersObj){
      console.log(c)
   }
   //iterating using the next() method
   let iter = customersObj[Symbol.iterator]();
   console.log(iter.next())
   console.log(iter.next())
   console.log(iter.next())
</script>

La sortie du code ci-dessus sera la suivante -

{firstName: "Sachin", lastName: "Tendulkar"}
{firstName: "Rahul", lastName: "Dravid"}
{
   done: false
   value: {
      firstName: "Sachin",
      lastName: "Tendulkar"
   }
}
{
   done: false
   value: {
      firstName: "Rahul",
      lastName: "Dravid"
   }
}
{done: true}

Générateur

Avant ES6, les fonctions en JavaScript suivaient un modèle de fin d'exécution. ES6 introduit des fonctions appelées Generator qui peuvent s'arrêter à mi-chemin puis continuer là où il s'est arrêté.

Un générateur préfixe le nom de la fonction avec un astérisque * et contient un ou plusieurs yielddéclarations. leyield mot-clé renvoie un objet itérateur.

Syntaxe

function * generator_name() {
   yield value1
   ...
   yield valueN
}

Exemple

L'exemple définit une fonction de générateur getMarksavec trois déclarations de rendement. Contrairement aux fonctions normales, legenerator function getMarks(), lorsqu'elle est invoquée, n'exécute pas la fonction mais renvoie un objet itérateur qui vous aide à exécuter du code à l'intérieur de la fonction de générateur.

Lors du premier appel à markIter.next()les opérations au début s'exécuteraient et l'instruction yield interrompt l'exécution du générateur. Appels ultérieurs aumarkIter.next() reprendra la fonction du générateur jusqu'au prochain yield expression.

<script>
   //define generator function
   function * getMarks(){
      console.log("Step 1")
      yield 10
      console.log("Step 2")
      yield 20
      console.log("Step 3")
      yield 30
      console.log("End of function")
   }
   //return an iterator object
      let markIter = getMarks()
   //invoke statements until first yield
      console.log(markIter.next())
   //resume execution after the last yield until second yield expression
      console.log(markIter.next())
   //resume execution after last yield until third yield expression
      console.log(markIter.next())
      console.log(markIter.next()) // iteration is completed;no value is returned
</script>

La sortie du code ci-dessus sera comme mentionné ci-dessous -

Step 1
{value: 10, done: false}
Step 2
{value: 20, done: false}
Step 3
{value: 30, done: false}
End of function
{value: undefined, done: true}

Exemple

L'exemple suivant crée une séquence infinie de nombres pairs à travers

* Fonction de générateur evenNumberGenerator.

Nous pouvons parcourir tous les nombres pairs en utilisant next() ou en utilisant for of boucle comme indiqué ci-dessous

<script>
   function * evenNumberGenerator(){
      let num = 0;
      while(true){
         num+=2
         yield num
      }
   }
   // display first two elements
   let iter = evenNumberGenerator();
   console.log(iter.next())
   console.log(iter.next())
   //using for of to iterate till 12
   for(let n of evenNumberGenerator()){
      if(n==12)break;
      console.log(n);
   }
</script>

La sortie du code ci-dessus sera la suivante -

{value: 2, done: false}
{value: 4, done: false}
2
4
6
8
10