ES6 - Iterador

Introducción a Iterator

El iterador es un objeto que nos permite acceder a una colección de objetos de uno en uno.

Los siguientes tipos integrados son iterables por defecto:

  • String
  • Array
  • Map
  • Set

Se considera un objeto iterable, si el objeto implementa una función cuya clave es [Symbol.iterator]y devuelve un iterador. Un bucle for ... of se puede utilizar para iterar una colección.

Ejemplo

El siguiente ejemplo declara una matriz, marca e itera a través de ella usando un for..of lazo.

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

La salida del código anterior será como se indica a continuación:

10
20
30

Ejemplo

El siguiente ejemplo declara una matriz, marca y recupera un objeto iterador. los[Symbol.iterator]()se puede utilizar para recuperar un objeto iterador. El método next () del iterador devuelve un objeto con'value' y 'done'propiedades. 'done' es booleano y devuelve verdadero después de leer todos los elementos de la colección.

<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 salida del código anterior será como se muestra a continuación:

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

Iterable personalizado

Ciertos tipos en JavaScript son iterables (por ejemplo, matriz, mapa, etc.) mientras que otros no lo son (por ejemplo, clase). Los tipos de JavaScript que no son iterables de forma predeterminada se pueden iterar utilizando el protocolo iterable.

El siguiente ejemplo define una clase llamada CustomerListque almacena varios objetos de cliente como una matriz. Cada objeto de cliente tiene propiedades firstName y lastName.

Para que esta clase sea iterable, la clase debe implementar [Symbol.iterator]()función. Esta función devuelve un objeto iterador. El objeto iterador tiene una funciónnext que devuelve un objeto {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 salida del código anterior será la siguiente:

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

Generador

Antes de ES6, las funciones en JavaScript seguían un modelo de ejecución hasta su finalización. ES6 introduce funciones conocidas como Generador que pueden detenerse a mitad de camino y luego continuar desde donde se detuvo.

Un generador antepone el nombre de la función con un carácter de asterisco * y contiene uno o más yielddeclaraciones. losyield La palabra clave devuelve un objeto iterador.

Sintaxis

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

Ejemplo

El ejemplo define una función de generador getMarkscon tres declaraciones de rendimiento. A diferencia de las funciones normales, elgenerator function getMarks(), cuando se invoca, no ejecuta la función pero devuelve un objeto iterador que le ayuda a ejecutar código dentro de la función generadora.

En la primera llamada a markIter.next()las operaciones al principio se ejecutarían y la declaración de rendimiento pausa la ejecución del generador. Las siguientes llamadas almarkIter.next() reanudará la función del generador hasta el próximo yield expresión.

<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 salida del código anterior será la que se menciona a continuación:

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}

Ejemplo

El siguiente ejemplo crea una secuencia infinita de números pares hasta

* Función de generador evenNumberGenerator.

Podemos iterar a través de todos los números pares usando next() o usando for of bucle como se muestra a continuación

<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 salida del código anterior será la siguiente:

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