ES6 - Iterador

Introdução ao Iterator

Iterator é um objeto que nos permite acessar uma coleção de objetos um de cada vez.

Os seguintes tipos integrados são, por padrão, iteráveis ​​-

  • String
  • Array
  • Map
  • Set

Um objeto é considerado iterable, se o objeto implementa uma função cuja chave é [Symbol.iterator]e retorna um iterador. Um loop for ... of pode ser usado para iterar uma coleção.

Exemplo

O exemplo a seguir declara uma matriz, marca e itera por meio dela usando um for..of ciclo.

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

A saída do código acima será conforme fornecido abaixo -

10
20
30

Exemplo

O exemplo a seguir declara uma matriz, marca e recupera um objeto iterador. o[Symbol.iterator]()pode ser usado para recuperar um objeto iterador. O método next () do iterador retorna um objeto com'value' e 'done'propriedades. 'done' é booleano e retorna true após a leitura de todos os itens da coleção.

<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>

A saída do código acima será conforme mostrado abaixo -

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

Iterável personalizado

Certos tipos em JavaScript são iteráveis ​​(por exemplo, matriz, mapa etc.), enquanto outros não (por exemplo, classe). Os tipos de JavaScript que não são iteráveis ​​por padrão podem ser iterados usando o protocolo iterável.

O exemplo a seguir define uma classe chamada CustomerListque armazena vários objetos de cliente como uma matriz. Cada objeto de cliente possui propriedades firstName e lastName.

Para tornar esta classe iterável, a classe deve implementar [Symbol.iterator]()função. Esta função retorna um objeto iterador. O objeto iterador tem uma funçãonext que retorna um 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>

A saída do código acima será a seguinte -

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

Gerador

Antes do ES6, as funções em JavaScript seguiam um modelo de execução completa. ES6 apresenta funções conhecidas como Gerador que pode parar no meio do caminho e continuar de onde parou.

Um gerador prefixa o nome da função com um asterisco * e contém um ou mais yieldafirmações. oyield palavra-chave retorna um objeto iterador.

Sintaxe

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

Exemplo

O exemplo define uma função geradora getMarkscom três declarações de rendimento. Ao contrário das funções normais, ogenerator function getMarks(), quando invocado, não executa a função, mas retorna um objeto iterador que o ajuda a executar o código dentro da função do gerador.

Na primeira chamada para markIter.next()as operações no início seriam executadas e a declaração de rendimento pausa a execução do gerador. Chamadas subsequentes para omarkIter.next() irá retomar a função do gerador até o próximo yield expressão.

<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>

A saída do código acima será conforme mencionado abaixo -

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}

Exemplo

O exemplo a seguir cria uma sequência infinita de números pares por meio de

* função do gerador evenNumberGenerator.

Podemos iterar por todos os números pares usando next() ou usando for of loop como mostrado abaixo

<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>

A saída do código acima será a seguinte -

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