Manejar cada artículo en una colección es una operación muy común. JavaScript proporciona muchas formas de iterar sobre una colección, desde simple para y para cada bucle a map (), filtro () y comprensiones de matriz (derivación de la matriz). En JavaScript 1.7, los iteradores y los generadores traen nuevos mecanismos iterativos en la sintaxis central de JavaScript, y también proporcionan mecanismos para personalizar el comportamiento de ... en y para cada bucles.
Iterador
Un iterador es un objeto que accede a un elemento en una secuencia de colección cada vez y rastrea la posición actual de las iteraciones en esa secuencia. En JavaScript Iterator es un objeto que proporciona un método Next (), que devuelve el siguiente elemento en la secuencia. Este método lanza una excepción de stopiteration cuando todos los elementos en la secuencia están atravesados.
Una vez que se crea el objeto Iterator, se puede llamar implícitamente repitiendo explícitamente Next (), o utilizando JavaScript para ... en y para cada bucle.
Se puede crear un iterador simple que itera sobre objetos y matrices usando iterator ()::
La copia del código es la siguiente:
var lang = {nombre: 'javascript', cumpleaños year: 1995};
var it = iterator (lang);
Una vez que se completa la inicialización, se puede llamar al método Next () para acceder a los pares de valor clave del objeto a su vez:
La copia del código es la siguiente:
var par = it.next (); // Los pares de valor clave son ["Nombre", "JavaScript"]
par = it.next (); // El par de valores clave es ["cumpleaños", 1995]
par = it.next (); // se lanzó una excepción de `stopiteration`
El para ... en bucle se puede usar para reemplazar la llamada explícita al método Next (). Cuando se lanza la excepción de stopiteration, el bucle terminará automáticamente.
La copia del código es la siguiente:
var it = iterator (lang);
para (par de var)
imprimir (par); // un par de valor clave [clave, valor] en él se emite cada vez
Si solo desea iterar sobre el valor clave del objeto, puede pasar el segundo parámetro a la función iterator (), con el valor verdadero:
La copia del código es la siguiente:
var it = iterator (lang, true);
para (clave var en él)
imprimir (clave); // Solo valor de clave de salida
Una ventaja de usar iterator () para acceder a objetos es que las propiedades personalizadas agregadas a Object.prototype no se incluyen en el objeto de secuencia.
Iterator () también se puede usar en una matriz:
La copia del código es la siguiente:
var langs = ['JavaScript', 'Python', 'Haskell'];
var it = iterator (langs);
para (par de var)
imprimir (par); // Solo salida de salida de iteración [índice, lenguaje] par de valores clave
Al igual que atravesar un objeto, el resultado de pasar verdadero al recorrido como segundo parámetro será el índice de matriz:
La copia del código es la siguiente:
var langs = ['JavaScript', 'Python', 'Haskell'];
var it = iterator (langs, true);
para (var i en él)
imprimir (i); // Salida 0, luego 1, luego 2
Use la palabra clave LET para asignar índices y valores para bloquear las variables por separado dentro del bucle, y también puede destruir la asignación (asignación de destrucción):
La copia del código es la siguiente:
var langs = ['JavaScript', 'Python', 'Haskell'];
var it = iterators (langs);
para (deja [i, lang] en él)
imprimir (i + ':' + lang); // Salida "0: JavaScript", etc.
Declarar un iterador personalizado
Algunos objetos que representan una colección de elementos deben iterarse de manera específica.
1. iterer sobre un objeto que representa un rango (rango) debe devolver el número contenido en este rango uno por uno.
2. Se puede acceder al nodo de la hoja de un árbol utilizando profundidad primero o de amplitud
3. Iterando sobre un objeto que representa los resultados de la consulta de la base de datos deberían devolver la fila por fila, incluso si el conjunto de resultados completo no se ha cargado en una sola matriz.
4. Los iteradores que actúan sobre una secuencia matemática infinita (como la secuencia de Fibonacci) deberían devolver los resultados uno tras otro sin crear una estructura de datos de longitud infinita.
JavaScript le permite escribir código que personaliza la lógica iterativa y aplicarlo a un objeto
Creamos un objeto de rango simple con dos valores:
La copia del código es la siguiente:
Rango de funciones (bajo, alto) {
this.low = bajo;
this.high = alto;
}
Ahora creamos un iterador personalizado que devuelve una secuencia de todos los enteros en el rango. La interfaz Iterator requiere que proporcionemos un método Next () para devolver el siguiente elemento en la secuencia o lanzar una excepción de stopiteration.
La copia del código es la siguiente:
function RangeIterator (Range) {
this.range = range;
this.current = this.range.low;
}
RangeIterator.prototype.next = function () {
if (this.current> this.range.high)
tirar la paro;
demás
devolver esto.Current ++;
};
Nuestro RangeIterator se instancia mediante una instancia de rango mientras se mantiene una propiedad actual para rastrear la ubicación de la secuencia actual.
Finalmente, para que el rangeiterator se combine con el rango, necesitamos agregar un método especial __iterator__ para el rango. Cuando intentamos iterar en un rango, se llamará y debe devolver una instancia de RangeIterator que implementa la lógica iterativa.
La copia del código es la siguiente:
Range.prototype .__ iterator__ = function () {
devolver nuevo RangeIterator (esto);
};
Después de completar nuestro iterador personalizado, podemos iterar en una instancia de alcance:
La copia del código es la siguiente:
rango var = nuevo rango (3, 5);
para (var i en el rango)
imprimir (i); // Salida 3, luego 4, luego 5
Generador: una mejor manera de construir iteradores
Aunque los iteradores personalizados son una herramienta útil, debe planificarlos cuidadosamente al crearlos porque deben mantenerse explícitamente.
El generador proporciona funciones potentes: le permite definir una función que contenga su propio algoritmo iterativo, y puede mantener automáticamente su estado.
Un generador es una función especial que puede usarse como una fábrica de iterador. Si una función contiene una o más expresiones de rendimiento, se llama generador (nota del traductor: Node.js también debe ser representada por * antes del nombre de la función).
NOTA: La palabra clave de rendimiento solo se puede usar para bloques de código en HTML que se incluyen en <script type = "aplicación/javaScript; versión = 1.7"> (o más tarde). Las etiquetas de secuencia de comandos XUL (XML Interface de usuario) no requieren especificar este bloque de código especial para acceder a estas características.
Cuando se llama a una función de generador, el cuerpo de la función no se ejecutará de inmediato, devolverá un objeto de iterador generador. Cada vez que se llama el método Next () de Generador-iterator, el cuerpo de la función se ejecutará a la siguiente expresión de rendimiento y luego devolverá su resultado. Cuando la función finaliza o encuentra una declaración de devolución, se lanzará una excepción de stopiteration.
Use un ejemplo para ilustrar mejor:
La copia del código es la siguiente:
function SimpleGenerator () {
rendimiento "primero";
rendimiento "segundo";
rendimiento "tercero";
para (var i = 0; i <3; i ++)
rendimiento i;
}
var g = simpleGenerator ();
imprimir (G.Next ()); // Salida "Primero"
imprimir (G.Next ()); // Salida "Segundo"
imprimir (G.Next ()); // Salida "tercero"
imprimir (G.Next ()); // Salida 0
imprimir (G.Next ()); // Salida 1
imprimir (G.Next ()); // Salida 2
imprimir (G.Next ()); // arrojar una excepción de stopiteration
Las funciones del generador pueden usarse directamente mediante una clase como método __iterator__, y pueden reducir efectivamente la cantidad de código donde se necesitan iteradores personalizados. Reescribamos el rango con el generador:
La copia del código es la siguiente:
Rango de funciones (bajo, alto) {
this.low = bajo;
this.high = alto;
}
Range.prototype .__ iterator__ = function () {
para (var i = this.low; i <= this.high; i ++)
rendimiento i;
};
rango var = nuevo rango (3, 5);
para (var i en el rango)
imprimir (i); // Salida 3, luego 4, luego 5
No todos los generadores terminarán, puede crear un generador que represente una secuencia infinita. El siguiente generador implementa una secuencia Fibonacci, en la que cada elemento es la suma de los dos primeros:
La copia del código es la siguiente:
función fibonacci () {
var fn1 = 1;
var fn2 = 1;
mientras (1) {
var corriente = fn2;
fn2 = fn1;
fn1 = fn1 + corriente;
rendimiento de corriente;
}
}
VAR secuencia = fibonacci ();
print (secuence.next ()); // 1
print (secuence.next ()); // 1
print (secuence.next ()); // 2
print (secuence.next ()); // 3
print (secuence.next ()); // 5
print (secuence.next ()); // 8
print (secuence.next ()); // 13
Las funciones del generador pueden tomar parámetros y utilizarán estos parámetros cuando la función se solicite por primera vez. El generador se puede terminar (haciendo que lance una excepción de stopiteration) utilizando la declaración de retorno. La siguiente variante Fibonacci () toma un parámetro límite opcional que termina la función cuando se activa la condición.
La copia del código es la siguiente:
función fibonacci (límite) {
var fn1 = 1;
var fn2 = 1;
mientras (1) {
var corriente = fn2;
fn2 = fn1;
fn1 = fn1 + corriente;
if (límite && current> límite)
devolver;
rendimiento de corriente;
}
}
Características avanzadas del generador
El generador puede calcular el valor de retorno de rendimiento en función de los requisitos, lo que hace que represente los requisitos de cálculo de secuencia previamente costosos, incluso la secuencia infinita que se muestra arriba.
Además del método Next (), el objeto Generador-iterator también tiene un método Send (), que puede modificar el estado interno del generador. El valor pasado para enviar () se tratará como el resultado de la última expresión de rendimiento y el generador se detendrá. Antes de pasar un valor especificado usando el método send (), debe llamar a Next () al menos una vez para iniciar el generador.
El siguiente generador Fibonacci usa el método Send () para reiniciar la secuencia:
La copia del código es la siguiente:
función fibonacci () {
var fn1 = 1;
var fn2 = 1;
mientras (1) {
var corriente = fn2;
fn2 = fn1;
fn1 = fn1 + corriente;
VAR RESET = Corriente de rendimiento;
if (reet) {
fn1 = 1;
fn2 = 1;
}
}
}
VAR secuencia = fibonacci ();
print (secuence.next ()); // 1
print (secuence.next ()); // 1
print (secuence.next ()); // 2
print (secuence.next ()); // 3
print (secuence.next ()); // 5
print (secuence.next ()); // 8
print (secuence.next ()); // 13
print (secuencia.send (true)); // 1
print (secuence.next ()); // 1
print (secuence.next ()); // 2
print (secuence.next ()); // 3
Nota: Curiosamente, llamar a enviar (indefinido) es exactamente lo mismo que llamar a Next (). Sin embargo, cuando se llama al método send () para iniciar un nuevo generador, se lanzará una excepción de typeError excepto indefinida.
Puede llamar al método de lanzamiento y pasar un atípico que debería lanzar para obligar al generador a lanzar una excepción. Esta excepción se arrojará desde el contexto actual y se detendrá al generador, similar a la ejecución de rendimiento actual, pero se reemplazará con una declaración de valor de lanzamiento.
Si no se encuentra el rendimiento durante el proceso de lanzar la excepción, la excepción se pasará hasta que se llame al método de lanzar () y, posteriormente, llamar a Siguiente () hará que se lance la excepción de stopiteration.
El generador tiene un método cercano () para obligar al generador a finalizar. Terminar un generador tendrá los siguientes efectos:
1. Todas las oraciones válidas finalmente en el generador se ejecutarán
2. Si la palabra finalmente arroja alguna excepción, excepto la stopiteration, la excepción se pasará a la persona que llama el método Close ().
3. El generador terminará
Expresiones generadoras
Una desventaja obvia de la derivación de la matriz es que causan que toda la matriz se construya en la memoria. La sobrecarga de la entrada a la derivación es insignificante cuando su sobrecarga es una pequeña matriz en sí; sin embargo, pueden surgir problemas cuando la matriz de entrada es grande o al crear un nuevo generador de matriz costoso (o infinito).
El generador permite que la informática perezosa calcule los elementos según sea necesario cuando sea necesario. Las expresiones del generador son sintácticamente casi las mismas que la derivación de la matriz: utiliza paréntesis en lugar de soportes cuadrados (y usa para ... en en lugar de cada ... in), pero crea un generador en lugar de una matriz para que el cálculo se pueda retrasar. Puede pensarlo como una breve sintaxis para crear un generador.
Supongamos que tenemos un iterador para iterar sobre una gran secuencia de enteros. Necesitamos crear un nuevo iterador para iterar sobre números pares. Una derivación de la matriz creará una matriz completa que contiene todos los números pares en la memoria:
La copia del código es la siguiente:
var dobles = [i * 2 para (i en él)];
La expresión del generador creará un nuevo iterador y calculará el valor uniforme según sea necesario cuando sea necesario:
La copia del código es la siguiente:
var it2 = (i * 2 para (i en él));
imprimir (it2.next ()); // El primer número par en él
imprimir (it2.next ()); // El segundo número par en él
Cuando un generador se usa como parámetro de una función, los paréntesis se usan como una llamada de función, lo que significa que se pueden omitir los paréntesis más externos:
La copia del código es la siguiente:
VAR result = Dosomething (i * 2 para (i en él));
Fin.