Resumo : neste tutorial, você aprenderá sobre iteradores JavaScript e como usá-los para processar uma sequência de dados com mais eficiência.
Os for
problemas do loop
Quando você tem uma matriz de dados, normalmente usa um for
loop para iterar sobre seus elementos. Por exemplo:
let ranks = ['A', 'B', 'C'];
for (let i = 0; i < ranks.length; i++) {
console.log(ranks[i]);
}
Linguagem de código: JavaScript ( javascript )
O for
loop usa a variável i
para rastrear o índice do ranks
array. O valor dos i
incrementos cada vez que o loop é executado, desde que o valor de i
seja menor que o número de elementos na ranks
matriz.
Este código é direto. No entanto, sua complexidade aumenta quando você aninha um loop dentro de outro loop. Além disso, acompanhar múltiplas variáveis dentro dos loops é propenso a erros.
ES6 introduziu uma nova construção de loop chamada for...of
para eliminar a complexidade do loop padrão e evitar os erros causados pelo controle dos índices do loop.
Para iterar sobre os elementos do ranks
array, você usa a seguinte for...of
construção:
for(let rank of ranks) {
console.log(rank);
}
Linguagem de código: JavaScript ( javascript )
É for...of
muito mais elegante que o for
loop porque mostra a verdadeira intenção do código – iterar sobre um array para acessar cada elemento na sequência.
Além disso, o for...of
loop tem a capacidade de criar um loop sobre qualquer objeto iterável , não apenas um array.
Para entender o objeto iterável, você precisa primeiro entender os protocolos de iteração.
Protocolos de iteração
Existem dois protocolos de iteração: protocolo iterável e protocolo iterador .
Protocolo iterador
Um objeto é um iterador quando implementa uma interface (ou API) que responde a duas perguntas:
- Sobrou algum elemento?
- Se houver, qual é o elemento?
Tecnicamente falando, um objeto é qualificado como iterador quando possui um next()
método que retorna um objeto com duas propriedades:
-
done
: um valor booleano que indica se há ou não mais elementos que possam ser iterados. -
value
: o elemento atual.
Cada vez que você chama next()
, ele retorna o próximo valor da coleção:
{ value: 'next value', done: false }
Linguagem de código: CSS ( css )
Se você chamar o next()
método após o último valor ter sido retornado, ele next()
retornará o objeto de resultado da seguinte forma:
{done: true: value: undefined}
Linguagem de código: CSS ( css )
O valor da done
propriedade indica que não há mais valor a ser retornado e o valor value
da propriedade está definido como undefined
.
Protocolo iterável
Um objeto é iterável quando contém um método chamado [Symbol.iterator]
que não aceita argumentos e retorna um objeto que está em conformidade com o protocolo do iterador.
O é um dos símbolos[Symbol.iterator]
integrados bem conhecidos no ES6.
Iteradores
Como o ES6 fornece iteradores integrados para os tipos de coleção Array
, Set
, e Map
, você não precisa criar iteradores para esses objetos.
Se você tiver um tipo personalizado e quiser torná-lo iterável para poder usar a for...of
construção de loop, será necessário implementar os protocolos de iteração.
O código a seguir cria um Sequence
objeto que retorna uma lista de números no intervalo de ( start
, end
) com um interval
entre os números subsequentes.
class Sequence {
constructor( start = 0, end = Infinity, interval = 1 ) {
this.start = start;
this.end = end;
this.interval = interval;
}
[Symbol.iterator]() {
let counter = 0;
let nextIndex = this.start;
return {
next: () => {
if ( nextIndex <= this.end ) {
let result = { value: nextIndex, done: false }
nextIndex += this.interval;
counter++;
return result;
}
return { value: counter, done: true };
}
}
}
};
Linguagem de código: JavaScript ( javascript )
O código a seguir usa o Sequence
iterador em um for...of
loop:
let evenNumbers = new Sequence(2, 10, 2);
for (const num of evenNumbers) {
console.log(num);
}
Linguagem de código: JavaScript ( javascript )
Saída:
2
4
6
8
10
Você pode acessar explicitamente o [Symbol.iterator]()
método conforme mostrado no script a seguir:
let evenNumbers = new Sequence(2, 10, 2);
let iterator = evenNumbers[Symbol.iterator]();
let result = iterator.next();
while( !result.done ) {
console.log(result.value);
result = iterator.next();
}
Linguagem de código: JavaScript ( javascript )
Limpando
Além do next()
método, [Symbol.iterator]()
pode retornar opcionalmente um método chamado return()
.
O return()
método é invocado automaticamente quando a iteração é interrompida prematuramente. É onde você pode colocar o código para limpar os recursos.
O exemplo a seguir implementa o return()
método para o Sequence
objeto:
class Sequence {
constructor( start = 0, end = Infinity, interval = 1 ) {
this.start = start;
this.end = end;
this.interval = interval;
}
[Symbol.iterator]() {
let counter = 0;
let nextIndex = this.start;
return {
next: () => {
if ( nextIndex <= this.end ) {
let result = { value: nextIndex, done: false }
nextIndex += this.interval;
counter++;
return result;
}
return { value: counter, done: true };
},
return: () => {
console.log('cleaning up...');
return { value: undefined, done: true };
}
}
}
}
Linguagem de código: JavaScript ( javascript )
O trecho a seguir usa o Sequence
objeto para gerar uma sequência de números ímpares de 1 a 10. No entanto, ele interrompe prematuramente a iteração. Como resultado, o return()
método é invocado automaticamente.
let oddNumbers = new Sequence(1, 10, 2);
for (const num of oddNumbers) {
if( num > 7 ) {
break;
}
console.log(num);
}
Linguagem de código: JavaScript ( javascript )
Saída:
1
3
5
7
cleaning up...
Neste tutorial, você aprendeu sobre o iterador JavaScript e como usar os protocolos de iteração para implementar lógica de iteração customizada.