O manuseio de cada item em uma coleção é uma operação muito comum. O JavaScript fornece muitas maneiras de iterar sobre uma coleção, do simples e para cada loop para mapear (), filter () e compreensões de matriz (derivação da matriz). No JavaScript 1.7, iteradores e geradores trazem novos mecanismos iterativos na sintaxe do JavaScript central e também fornecem mecanismos para personalizar o comportamento de para ... dentro e para cada loops.
Iterador
Um iterador é um objeto que acessa um elemento em uma sequência de coleta sempre e rastreia a posição atual das iterações nessa sequência. No JavaScript Iterator é um objeto que fornece um método próximo (), que retorna o próximo elemento na sequência. Este método lança uma exceção de parada quando todos os elementos da sequência são atravessados.
Depois que o objeto Iterador é criado, ele pode ser chamado implicitamente repetindo explicitamente a seguir () ou usando o JavaScript para ... em e para cada loop.
Um iterador simples que itera sobre objetos e matrizes pode ser criado usando o iterator ():
A cópia do código é a seguinte:
var Lang = {Nome: 'JavaScript', aniversário: 1995};
var it = iterator (lang);
Depois que a inicialização estiver concluída, o método Next () pode ser chamado para acessar os pares de valor-chave do objeto, por sua vez:
A cópia do código é a seguinte:
var par = it.Next (); // pares-chave-chave são ["nome", "javascript"]
par = it.next (); // O par de valores-chave é ["Aniversário", 1995]
par = it.next (); // Uma exceção `StopIeration` foi lançada
O loop for… pode ser usado para substituir a chamada explícita para o método Next (). Quando a exceção de parada é lançada, o loop será encerrado automaticamente.
A cópia do código é a seguinte:
var it = iterator (lang);
para (vara var)
impressão (par); // um par de chaves-chave [chave, valor] é emitida a cada vez
Se você deseja iterar apenas o valor da chave do objeto, poderá passar o segundo parâmetro na função iterator (), com o valor true:
A cópia do código é a seguinte:
var it = iterator (lang, true);
para (tecla var)
impressão (chave); // Somente o valor da tecla de saída
Uma vantagem do uso do iterator () para acessar objetos é que as propriedades personalizadas adicionadas ao object.prototype não estão incluídas no objeto de sequência.
Iterator () também pode ser usado em uma matriz:
A cópia do código é a seguinte:
var Langs = ['JavaScript', 'Python', 'Haskell'];
var it = iterator (langs);
para (vara var)
impressão (par); // Somente saída de iteração [índice, idioma] par de valores-chave
Assim como atravessar um objeto, o resultado de passar verdadeiro para a travessia como um segundo parâmetro será o índice de matriz:
A cópia do código é a seguinte:
var Langs = ['JavaScript', 'Python', 'Haskell'];
var it = iterator (langs, true);
para (var i nele)
impressão (i); // saída 0, então 1, depois 2
Use a palavra -chave Let para atribuir índices e valores para bloquear variáveis separadamente dentro do loop, e você também pode destruir a atribuição (atribuição de destruição):
A cópia do código é a seguinte:
var Langs = ['JavaScript', 'Python', 'Haskell'];
var it = iterators (langs);
para (vamos [eu, lang] nele)
imprimir (i + ':' + lang); // saída "0: javascript" etc.
Declare um iterador personalizado
Alguns objetos que representam uma coleção de elementos devem ser iterados de maneira especificada.
1. Itera sobre um objeto que representa um intervalo (intervalo) deve retornar o número contido neste intervalo um por um.
2. O nó da folha de uma árvore pode ser acessado usando a profundidade primeiro ou a largura-primeiro
3. Iterando sobre um objeto que representa os resultados da consulta do banco de dados deve retornar a linha por linha, mesmo que todo o conjunto de resultados não tenha sido carregado em uma única matriz.
4. Os iteradores que atuam em uma sequência matemática infinita (como a sequência de Fibonacci) devem retornar os resultados um após o outro sem criar uma estrutura de dados de comprimento infinito.
O JavaScript permite escrever código que personaliza a lógica iterativa e a aplique a um objeto
Criamos um objeto de intervalo simples com dois valores:
A cópia do código é a seguinte:
faixa de função (baixa, alta) {
this.low = baixo;
this.High = alto;
}
Agora, criamos um iterador personalizado que retorna uma sequência de todos os números inteiros do intervalo. A interface do iterador exige que forneçamos um método próximo () para retornar o próximo elemento na sequência ou lançar uma exceção de parada.
A cópia do código é a seguinte:
Função Riditerator (Range) {
this.Range = range;
this.current = this.range.low;
}
Rangeterator.prototype.Next = function () {
if (this.current> this.range.high)
lançar a parada;
outro
Retorne this.Current ++;
};
Nosso rangereator é instanciado por uma instância de intervalo, mantendo uma propriedade atual para rastrear a localização da sequência atual.
Finalmente, para que o rangereator seja combinado com o intervalo, precisamos adicionar um método __Iterator__ especial ao alcance. Quando tentamos iterar em um intervalo, ele será chamado e deve retornar uma instância de rangereator que implementa a lógica iterativa.
A cópia do código é a seguinte:
Range.prototype .__ iterator__ = function () {
retornar novo rangereator (this);
};
Depois de concluir nosso iterador personalizado, podemos iterar em uma instância de escopo:
A cópia do código é a seguinte:
var range = novo intervalo (3, 5);
para (var i no intervalo)
impressão (i); // saída 3, depois 4, depois 5
Gerador: uma maneira melhor de construir iteradores
Embora os iteradores personalizados sejam uma ferramenta útil, você precisa planejá -los cuidadosamente ao criá -los, porque eles precisam ser mantidos explicitamente.
O gerador fornece funções poderosas: permite definir uma função que contém seu próprio algoritmo iterativo e pode manter automaticamente seu estado.
Um gerador é uma função especial que pode ser usada como uma fábrica de iterador. Se uma função contiver uma ou mais expressões de rendimento, é chamada de gerador (Nota do tradutor: Node.js também precisará ser representado por * antes do nome da função).
Nota: A palavra -chave de rendimento pode ser usada apenas para blocos de código no HTML incluídos em <script type = "Application/javascript; versão = 1.7"> (ou posterior). As tags de script XUL (XML User Interface Language) não exigem especificar este bloco de código especial para acessar esses recursos.
Quando uma função de gerador é chamada, o corpo da função não será executado imediatamente, ele retornará um objeto gerador-armador. Cada vez que o método próximo () do gerador-armador é chamado, o corpo da função será executado para a próxima expressão de rendimento e retorna seu resultado. Quando a função termina ou encontrar uma declaração de retorno, uma exceção de parada de stopiber será lançada.
Use um exemplo para ilustrar melhor:
A cópia do código é a seguinte:
função SimpleGenerator () {
render "primeiro";
render "segundo";
render "terceiro";
for (var i = 0; i <3; i ++)
rendimento i;
}
var g = SimpleGenerator ();
print (g.Next ()); // Saída "Primeiro"
print (g.Next ()); // Saída "Segundo"
print (g.Next ()); // Saída "Terceira"
print (g.Next ()); // saída 0
print (g.Next ()); // Saída 1
print (g.Next ()); // Saída 2
print (g.Next ()); // Lança uma exceção de stopiberation
As funções do gerador podem ser usadas diretamente por uma classe como método __Iterator__ e podem reduzir efetivamente a quantidade de código onde os iteradores personalizados são necessários. Vamos reescrever o intervalo usando o gerador:
A cópia do código é a seguinte:
faixa de função (baixa, alta) {
this.low = baixo;
this.High = alto;
}
Range.prototype .__ iterator__ = function () {
para (var i = this.low; i <= this.high; i ++)
rendimento i;
};
var range = novo intervalo (3, 5);
para (var i no intervalo)
impressão (i); // saída 3, depois 4, depois 5
Nem todos os geradores terminam, você pode criar um gerador que represente uma sequência infinita. O gerador a seguir implementa uma sequência de Fibonacci, na qual cada elemento é a soma dos dois primeiros:
A cópia do código é a seguinte:
função fibonacci () {
var fn1 = 1;
var fn2 = 1;
enquanto (1) {
Var Current = fn2;
fn2 = fn1;
fn1 = fn1 + corrente;
rendimento de corrente;
}
}
var sequence = fibonacci ();
print (sequence.Next ()); // 1
print (sequence.Next ()); // 1
print (sequence.Next ()); // 2
print (sequence.Next ()); // 3
print (sequence.Next ()); // 5
print (sequence.Next ()); // 8
print (sequence.Next ()); // 13
As funções do gerador podem assumir parâmetros e usarão esses parâmetros quando a função for chamada pela primeira vez. O gerador pode ser encerrado (fazendo com que ele jogue uma exceção de parada) usando a instrução RETURN. A seguinte variante fibonacci () leva um parâmetro de limite opcional que termina a função quando a condição é acionada.
A cópia do código é a seguinte:
função fibonacci (limite) {
var fn1 = 1;
var fn2 = 1;
enquanto (1) {
Var Current = fn2;
fn2 = fn1;
fn1 = fn1 + corrente;
if (limite && atual> limite)
retornar;
rendimento de corrente;
}
}
Recursos avançados do gerador
O gerador pode calcular o valor do retorno do rendimento com base nos requisitos, o que o faz representar os requisitos de cálculo de sequência anteriormente caros, mesmo a sequência infinita mostrada acima.
Além do método Next (), o objeto gerador -erator também possui um método send (), que pode modificar o estado interno do gerador. O valor aprovado para envio () será tratado como resultado da última expressão de rendimento e o gerador será pausado. Antes de passar por um valor especificado usando o método send (), você deve ligar para o próximo () pelo menos uma vez para iniciar o gerador.
O seguinte gerador Fibonacci usa o método send () para reiniciar a sequência:
A cópia do código é a seguinte:
função fibonacci () {
var fn1 = 1;
var fn2 = 1;
enquanto (1) {
Var Current = fn2;
fn2 = fn1;
fn1 = fn1 + corrente;
var reset = rendimento de corrente;
if (reset) {
fn1 = 1;
fn2 = 1;
}
}
}
var sequence = fibonacci ();
print (sequence.Next ()); // 1
print (sequence.Next ()); // 1
print (sequence.Next ()); // 2
print (sequence.Next ()); // 3
print (sequence.Next ()); // 5
print (sequence.Next ()); // 8
print (sequence.Next ()); // 13
print (sequence.send (true)); // 1
print (sequence.Next ()); // 1
print (sequence.Next ()); // 2
print (sequence.Next ()); // 3
NOTA: Curiosamente, a chamada de send (indefinida) é exatamente a mesma que a chamada a seguir (). No entanto, quando o método send () é chamado para iniciar um novo gerador, uma exceção do TypeError será lançada, exceto indefinida.
Você pode chamar o método de arremesso e passar por um outlier que ele deve jogar para forçar o gerador a lançar uma exceção. Essa exceção será lançada do contexto atual e fez uma pausa no gerador, semelhante à execução atual do rendimento, mas substituída por uma instrução de valor de arremesso.
Se o rendimento não for encontrado durante o processo de lançamento da exceção, a exceção será aprovada até que o método arreme () seja chamado e, posteriormente, chamando a próxima () fará com que a exceção de parada seja lançada.
O gerador possui um método Close () para forçar o gerador a terminar. Terminar um gerador terá os seguintes efeitos:
1. Todas as sentenças válidas finalmente no gerador serão executadas
2. Se a palavra finalmente lançar qualquer exceção, exceto a parada, a exceção será passada para o método de chamador do Close ().
3. O gerador terá rescisão
Expressões geradoras
Uma desvantagem óbvia da derivação da matriz é que eles fazem com que toda a matriz seja construída na memória. A sobrecarga de entrada na derivação é insignificante quando sua sobrecarga é uma pequena matriz - no entanto, os problemas podem surgir quando a matriz de entrada é grande ou ao criar um novo gerador de matriz caro (ou infinito).
O gerador permite que a computação preguiçosa calcule os elementos conforme necessário quando necessário. As expressões geradoras são sintaticamente quase a mesma que a derivação da matriz - usa parênteses em vez de colchetes (e usa para ... in em vez de para cada ... in) - mas cria um gerador em vez de uma matriz para que o cálculo possa ser atrasado. Você pode pensar nisso como uma breve sintaxe para criar um gerador.
Suponha que tenhamos um iterador para iterar sobre uma enorme sequência de números inteiros. Precisamos criar um novo iterador para iterar sobre números pares. Uma derivação da matriz criará uma matriz inteira contendo todos os números pares na memória:
A cópia do código é a seguinte:
var duplos = [i * 2 para (i nele)];
A expressão do gerador criará um novo iterador e calculará o valor uniforme conforme necessário quando necessário:
A cópia do código é a seguinte:
var it2 = (i * 2 para (i nele));
print (it2.next ()); // o primeiro número par
print (it2.next ()); // o segundo número par
Quando um gerador é usado como um parâmetro de uma função, os parênteses são usados como chamada de função, o que significa que os parênteses mais externos podem ser omitidos:
A cópia do código é a seguinte:
var resultado = doSomething (i * 2 para (i nele));
Fim.