Este artigo traz a você conhecimento relevante sobre JavaScript. Ele apresenta principalmente o gerador e iterador front-end de JavaScript. Espero que seja útil.
Entrada front-end (vue) para curso de domínio: entre no aprendizado
O Iterator fornece um mecanismo de interface unificado para fornecer um mecanismo de acesso unificado para várias estruturas de dados diferentes.
Definir Iterator é fornecer um objeto com um método next(). Cada vez que next() for chamado, um objeto de resultado será retornado. .
função makeIterator(Array){ deixe índice = 0; retornar { próximo: função(){ retornar ( Array.length > índice? {valor: Matriz[índice++]}: {feito: verdadeiro} ) } } } deixe iterador = makeIterator(['1','2']) console.log(iterator.next()); // {valor: '1'} console.log(iterator.next()); // {valor: '2'} console.log(iterator.next()); // {feito: verdadeiro}
A função do Iterador:
Fornece uma interface de acesso unificada e simples para diversas estruturas de dados;
Permite que os membros de uma estrutura de dados sejam organizados em uma determinada ordem;
para consumo por por...de
ES6 fornece a instrução for of para percorrer o objeto iterador. Usaremos a instrução for of para percorrer o iterador criado acima:
deixe iterador = makeIterator(['1','2']) for (deixe o valor do iterador) { console.log(valor); } // iterador não é iterável
O resultado é um erro dizendo que o iterador não é iterável. Por que isso? ES6 estipula que a interface Iterator padrão é implantada na propriedade Symbol.iterator da estrutura de dados. Se uma estrutura de dados tiver a propriedade Symbol.iterator, a estrutura de dados pode ser percorrida.
Transformamos o makeIterator personalizado da seguinte maneira:
const MakeIterator = (Array) => ({ [Símbolo.iterador](){ deixe índice = 0; retornar { próximo(){ deixe comprimento = Array.length; if(índice <comprimento){ retornar {valor: Matriz[índice++]} }outro{ retornar {concluído: verdadeiro} } } } } }) for(deixe o valor de MakeIterator([1,2])){ console.log(valor) } //1 //2
Adicionamos um método de retorno ao MakeIterator. Se o loop for...of terminar mais cedo (geralmente devido a um erro ou a uma instrução break), o método return() será chamado para encerrar a travessia.
Com base nesse recurso, se um objeto precisar limpar ou liberar recursos antes de concluir a travessia, podemos implantar o método return() e incluir o fechamento do arquivo quando a leitura do arquivo falhar.
const MakeIterator = (Array) => ({ [Símbolo.iterador](){ deixe índice = 0; retornar { próximo(){ deixe comprimento = Array.length; if(índice <comprimento){ retornar {valor: Matriz[índice++]} }outro{ retornar {concluído: verdadeiro} } }, retornar(){ retornar {concluído: verdadeiro} } } } }) for(deixe o valor de MakeIterator([1, 2, 3])){ console.log(valor) // 1 // Método 1 quebrar; // Método 2 // lança novo Erro('erro'); }
variedade
Definir
Mapa
Objetos semelhantes a array, como objetos de argumentos, objetos DOM NodeList, objetos typedArray
// argumentos objeto função soma(){ for(deixe o valor dos argumentos){ console.log(valor) } } soma(1,2) //1 //2 // objeto typedArray let typeArry = new Int8Array(2); tipoArry[0] = 1; tipoArry[1] = 2; for(deixe o valor de typeArry){ console.log(valor) } //1 //2
Objeto gerador
função*gen(){ rendimento 1; rendimento 2; } for(deixe o valor de gen()){ console.log(valor) }
Corda
P: Por que o Object não tem um Iterator nativo?
R: A razão pela qual o Object não implementa a interface do Iterator por padrão é porque é incerto qual propriedade do objeto é percorrida primeiro e qual propriedade é percorrida depois.
Em essência, o atravessador é um processo linear. Para qualquer estrutura de dados não linear, a implantação da interface do atravessador é equivalente à implantação de uma transformação linear.
Porém, a rigor, a interface do atravessador de implantação de objeto não é necessária, pois neste momento o objeto é realmente usado como uma estrutura de Mapa. O ES5 não possui uma estrutura de Mapa, mas o ES6 a fornece nativamente.
Atribuição de desestruturação
deixe definir = new Set().add('a').add('b').add('c'); deixe [x,y] = definir; // x='a';
operador de propagação
var str = 'olá'; [...str] // ['h','e','l','l','o']
O operador spread chama a interface Iterator, então Object não implanta a interface Iterator, então por que o operador... pode ser usado?
Motivo: Existem dois tipos de operadores de spread
Um é usado no caso de parâmetros de função e expansão de array. Nesse caso, o objeto deve ser iterável (iterável).
A outra é para expansão de objetos, ou seja, forma {...obj}. Nesse caso, o objeto precisa ser enumerável (enumerável).
deixe obj1 = { nome: 'qianxun' } deixe obj2 = { idade: 3 } // O objeto array é enumerável let obj = {...obj1, ...obj2} console.log(obj) //{nome: 'qianxun', idade: 3} // Objetos comuns não são iteráveis por padrão let obj = [...obj1, ...obj2] console.log(obj) // objeto não é iterável
função forOf(obj, cb){ deixe iteratorValue = obj[Symbol.iterator](); deixe resultado = iteratorValue.next() while(!resultado.done){ cb(resultado.valor) resultado = iteratorValue.next() } } forOf([1,2,3], (valor)=>{ console.log(valor) }) //1 //2 //3
Conceitualmente
A função geradora é uma solução de programação assíncrona fornecida pelo ES6. A função Generator é uma máquina de estados que encapsula vários estados internos;
A função Generator também é uma função de geração de objeto atravessador, que retorna um objeto atravessador após a execução.
formal
1. Existe um asterisco entre a palavra-chave da função e o nome da função;
2. Expressões de rendimento são usadas dentro do corpo da função para definir diferentes estados internos.
função* gerador simples(){ rendimento 1; rendimento 2; } gerador simples()
Como acima, criamos um Gerador simples e o exploramos com duas perguntas:
O que acontece depois que a função Gerador é executada?
O que a expressão de rendimento na função faz?
função* gerador simples(){ console.log('olá mundo'); rendimento 1; rendimento 2; } deixe gerador = simpleGenerator() // simpleGenerator {<suspended}} console.log(gerador.next()) // olá mundo // {valor: 1, concluído: falso} console.log(gerador.next()) // {valor: 2, concluído: falso}
A função geradora do gerador retorna um objeto gerador após a execução, enquanto a função comum executará diretamente o código dentro da função cada vez que o próximo método do objeto gerador for chamado, a função será executada até que a próxima palavra-chave yield interrompa a execução, e um objeto {valor: Valor, concluído: Boolean}.
A expressão de rendimento em si não tem valor de retorno ou sempre retorna indefinido. O próximo método pode receber um parâmetro, que será tratado como o valor de retorno da expressão de rendimento anterior. Através dos parâmetros do próximo método, diferentes valores podem ser injetados de fora para dentro em diferentes estágios da função Gerador para ajustar o comportamento da função. Como os parâmetros do próximo método representam o valor de retorno da expressão de rendimento anterior, a passagem de parâmetros é inválida na primeira vez que o próximo método for usado.
função soma(x){ função de retorno(y){ retornar x + y; } } console.log(soma(1)(2)) // Use o próximo parâmetro para reescrever a função* sum(x){ seja y = rendimento x; enquanto(verdadeiro){ y = rendimento x + y; } } deixe gen = soma (2) console.log(gen.next()) // 2 console.log(gen.next(1)) // 3 console.log(gen.next(2)) // 4
O papel da expressão de rendimento: definir o estado interno e pausar a execução A diferença entre a expressão de rendimento e a instrução de retorno.
A expressão yield significa que a função pausa a execução e continua a executar de trás para frente a partir dessa posição na próxima vez, enquanto a instrução return não tem a função de memória de posição.
Em uma função, apenas uma instrução return pode ser executada, mas várias expressões de rendimento podem ser executadas.
Qualquer função pode usar a instrução return. A expressão yield só pode ser usada na função Generator. Se usada em outro lugar, um erro será relatado.
Se a expressão de rendimento participar da operação, coloque-a entre parênteses; se for usada como parâmetro de função ou colocada no lado direito da expressão de atribuição, os parênteses não poderão ser incluídos;
função *gen(){ console.log('olá' + rendimento) × console.log('olá' + (rendimento)) √ console.log('olá' + rendimento 1) × console.log('olá' + (rendimento 1)) √ foo(rendimento 1) √ parâmetro const = rendimento 2 √ }
Com base no fato de que a função geradora Generator pode suportar múltiplos rendimentos, podemos implementar um cenário onde uma função tem múltiplos valores de retorno:
função* geração(num1, num2){ rendimento num1 + num2; rendimento num1 - num2; } deixe res = gen(2, 1); console.log(res.next()) // {valor: 3, concluído: falso} console.log(res.next()) // {valor: 1, concluído: falso}
Como a função Generator é a função de geração do iterador, o Generator pode ser atribuído à propriedade Symbol.iterator do objeto, para que o objeto tenha a interface Iterator. O código de implementação do gerador é mais conciso.
deixe obj = { nome: 'qianxun', idade: 3, [Símbolo.iterador]: function(){ deixe isso = isso; deixe chaves = Object.keys (aquele) deixe índice = 0; retornar { próximo: função(){ índice de retorno <keys.length? {valor: isso[chaves[índice++]], concluído: falso}: {valor: indefinido, concluído: verdadeiro} } } } } for(deixe o valor de obj){ console.log(valor) }
Gerador:
deixe obj = { nome: 'qianxun', idade: 3, [Símbolo.iterador]: função* (){ deixe chaves = Object.keys (este) for(deixe i=0; i< chaves.comprimento; i++){ produza isto[chaves[i]]; } } } for(deixe o valor de obj){ console.log(valor) }
O método
return()
pode retornar o valor fornecido e encerrar a função Geradora de travessia.
função*gen() { rendimento 1; rendimento 2; rendimento 3; } var g = gen(); g.next() // { valor: 1, concluído: falso } // Se nenhum parâmetro for fornecido quando o método return() for chamado, o atributo value do valor de retorno será indefinido g.return('foo') // { valor: "foo", concluído: true } g.next() // { valor: indefinido, concluído: verdadeiro }
Se houver try...finally
dentro da função Gerador e o bloco de código try
estiver sendo executado, return()
fará com que finally
seja inserido imediatamente. Após a execução, toda a função será encerrada.
função* números () { rendimento 1; tentar { rendimento 2; rendimento 3; } finalmente { rendimento 4; rendimento 5; } rendimento 6; } var g = números(); g.next() // { valor: 1, concluído: falso } g.next() // { valor: 2, concluído: false } g.return(7) // {valor: 4, concluído: falso } g.next() // { valor: 5, concluído: falso } g.next() // { valor: 7, concluído: verdadeiro }
Se você quiser chamar outra função Generator dentro da função Generator. Precisamos concluir manualmente a travessia dentro do corpo da função do primeiro. Se a chamada da função estiver aninhada em vários níveis, a escrita será complicada e difícil de ler. O ES6 fornece expressões yield* como solução.
Delegar a outros geradores
função* g1() { rendimento 2; rendimento 3; } função* g2() { rendimento 1; rendimento* g1(); rendimento 4; } iterador const = g2(); console.log(iterator.next()); // { valor: 1, concluído: false } console.log(iterator.next()); // { valor: 2, concluído: false } console.log(iterator.next()); // { valor: 3, concluído: false } console.log(iterator.next()); // { valor: 4, concluído: false } console.log(iterator.next()); // { valor: indefinido, concluído: verdadeiro }
Delegar para outros objetos iteráveis
função*gen(){ rendimento* [1,2,3] } console.log(gen().next()) // {valor: 1, concluído: falso}
A função Generator retorna um atravessador ES6 estipula que este atravessador é uma instância da função Generator e herda os métodos no objeto Generator.prototype, mas não pode obter as propriedades deste porque este é o objeto global neste momento, não a instância. objeto.
função*gen(){ isto.a = 1 } gen.prototype.say=função(){ console.log('oi') } deixe obj = gen() console.log(obj instância de gen) // verdadeiro obj.say() // oi obj.próximo() console.log(obj.a) //indefinido
Se quiser acessar as propriedades da instância como um construtor, você pode modificar isso para vinculá-la ao Generator.prototype.
função*gen(){ isto.a = 1 } gen.prototype.say=função(){ console.log('oi') } deixe obj = gen.call (gen.prototype) console.log(obj instância de gen) // verdadeiro obj.say() // oi obj.próximo() console.log(obj.a) //1
função* EstadoMáquina(estado){ deixe a transição; enquanto(verdadeiro){ if(transição === "INCREMENTO"){ estado++; }else if(transição === "DECREMENTO"){ estado--; } transição = estado de rendimento; } } iterador const = StateMachine(0); console.log(iterator.next()); console.log(iterator.next('INCREMENT')); console.log(iterator.next('DECREMENT'));