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