Iteração refere-se ao processo de extração contínua de dados de um conjunto de dados em uma determinada ordem.
Então, qual é a diferença entre iteração e travessia?
Em JavaScript, um iterador é um objeto que pode chamar next
método para implementar a iteração. retorna um objeto An com duas propriedades.
value
: O próximo valor do objeto iteráveldone
: Indica se todos os dados foram recuperados. false
significa que ainda há dados, true
significa que não há dados posteriormente.para gerar iteradores por meio da função de fábrica de iteradores Symbol.iterator
no objeto iterável.
const arr = []console.log(arr)
const arr = [1, 2, 3] const iter1 = arr[Symbol.iterator]() // Gere um iterador por meio da função de fábrica do iterador `Symbol.iterator`. console.log(iter1) console.log(iter1.next()) console.log(iter1.next()) console.log(iter1.next()) console.log(iter1.next()) console.log('%c%s', 'cor:vermelho;tamanho da fonte:24px;', '================') const meumapa = novo mapa() meumapa.set('nome', 'clz') meumapa.set('idade', 21) const iter2 = mymap[Symbol.iterator]() // Gere um iterador por meio da função de fábrica do iterador `Symbol.iterator`. console.log(iter2) console.log(iter2.next()) console.log(iter2.next()) console.log(iter2.next())
Pode-se verificar que o iterador é concluído após obter o último valor, ou seja, quando o próximo value
do iterador é undefined
.
No entanto, a declaração acima não é muito precisa e não é concluída quando o próximo value
do iterador é undefined
. Você também precisa determinar se realmente não há valor ou se há um valor undefined
no objeto iterável. Se houver um valor no objeto iterável que seja undefined
, ele não será concluído neste momento.
const arr = [1, 2, 3, indefinido] const iter1 = arr[Symbol.iterator]() // Gere um iterador por meio da função de fábrica do iterador `Symbol.iterator`. console.log(iter1) console.log(iter1.next()) console.log(iter1.next()) console.log(iter1.next()) console.log(iter1.next()) console.log(iter1.next())
podem chamar a função de fábrica do iterador várias vezes sem interferir uns nos outros para gerar vários iteradores. Cada iterador representa uma travessia ordenada única do objeto iterável. Diferentes iteradores não interferem entre si e apenas percorrerão objetos iteráveis de forma independente .
const arr = [1, 2, 3] const iter1 = arr[Symbol.iterator]() // Gere um iterador por meio da função de fábrica do iterador `Symbol.iterator`. const iter2 = arr[Symbol.iterator]() console.log('Iterador1:', iter1.next()) console.log('Iterador2:', iter2.next()) console.log('Iterador1:', iter1.next()) console.log('Iterador2:', iter2.next())
const arr = [1, 2, 3] const iter = arr[Symbol.iterator]() for (const i do iter) { console.log(i) // Saída 1, 2, 3 em sequência }
Se o objeto iterável for modificado durante a iteração, o resultado obtido pelo iterador também será modificado.
const arr = [1, 2, 3] console.log(arr) const iter = arr[Symbol.iterator]() console.log(iter.next()) arr[1] = 999 console.log(iter.next()) console.log(iter.next())
Quando iteramos para done: true
, um erro será relatado ao chamar next
ou nada será retornado?
No entanto, não, o iterador estará em um estado concluído, mas não concluído . done: true
significa que foi concluído, mas next
ainda pode ser chamado no futuro, embora o resultado seja sempre { value: undefined, done: true }
. É por isso que se diz que foi feito, mas não feito .
const arr = [1, 2, 3] const iter = arr[Symbol.iterator]() console.log(iter.next()) console.log(iter.next()) console.log(iter.next()) console.log(iter.next()) console.log(iter.next()) console.log(iter.next())
A partir do exemplo acima, podemos saber que o iterador é gerado por meio da função de fábrica do iterador Symbol.iterator
, portanto, precisamos implementar uma função de fábrica do iterador e, em seguida, o iterador pode chamar o next
método, então você também precisa implemente um next
método Quanto à função de fábrica do iterador, ela na verdade retorna a instância this
diretamente.
Exemplo de contador:
class Counter { construtor(limite) { esta.contagem = 1 este.limit = limite} próximo() { if (this.count <= this.limit) { retornar { feito: falso, valor: this.count++ } } outro { return {concluído: verdadeiro, valor: indefinido} } } [Símbolo.iterador]() { devolva isso }}
contador const = novo contador (3) const iter = contador[Symbol.iterator]() console.log(iter.next()) console.log(iter.next()) console.log(iter.next()) console.log(iter.next()) console.log(iter.next())
À primeira vista, não há problema, mas se usarmos for-of
para percorrer, podemos encontrar o problema.
const contador = new Contador(3)for (seja i do contador) { console.log(i)}console.log('Outra iteração:')for (seja eu do contador) { console.log(i)}
Usar for-of
também o torna descartável. Isso ocorre porque count
é uma variável desta instância, então a mesma variável é usada em ambas as iterações. Porém, após o primeiro loop da variável, ela excedeu o limite, então você não obterá nada usando for-of
. novamente. O resultado foi.
Você pode colocar a variável count
em um encerramento e depois retornar o iterador através do encerramento, de forma que cada iterador criado corresponda a um novo contador.
classe Contador { construtor(limite) { este.limit = limite} [Símbolo.iterador]() { vamos contar = 1 limite const = this.limit return { // A função de fábrica do iterador deve retornar um objeto com um método next, porque a iteração é realmente implementada chamando o método next() { if (contagem <= limite) { retornar { feito: falso, valor: contagem++ } } outro { return {concluído: verdadeiro, valor: indefinido} } } } }}
Teste
const contador = new Counter(3)for (seja i do contador) { console.log(i)}console.log('Outra iteração:')for (seja eu do contador) { console.log(i)}
é como usar for-of
. O iterador chamará next
método de maneira inteligente. Quando o iterador terminar antecipadamente, ele também chamará o método return
.
[Símbolo.iterador]() { vamos contar = 1 limite const = this.limit return { // A função de fábrica do iterador deve retornar um objeto com um método next, porque a iteração é realmente implementada chamando o método next() { if (contagem <= limite) { retornar { feito: falso, valor: contagem++ } } outro { return {concluído: verdadeiro, valor: indefinido} } }, retornar() { console.log('Término do iterador antecipadamente') retornar {feito: verdadeiro} } }}
Testar
contador const = new Counter(5)for (seja i do contador) { se (eu === 3) { quebrar; } console.log(i)}
Se o iterador não estiver fechado, você poderá continuar a iteração de onde parou . Os iteradores de array não podem ser fechados.
const arr = [1, 2, 3, 4, 5]const iter = arr[Symbol.iterator]()iter.return = function () { console.log('Sair do iterador mais cedo') retornar { feito: verdadeiro }}for (const i do iter) { console.log(i) se (eu === 2) { quebrar }}for (const i do iter) { console.log(i)}