¿Un iterable es lo mismo que un iterador, o son diferentes?
Parece, según las especificaciones , un iterable es un objeto, por ejemplo, obj
tal que se obj[Symbol.iterator]
refiere a una función, de modo que cuando se invoca, devuelve un objeto que tiene un next
método que puede devolver un {value: ___, done: ___}
objeto:
function foo() {
let i = 0;
const wah = {
next: function() {
if (i <= 2) return { value: (1 + 2 * i++), done: false }
else return { value: undefined, done: true }
}
};
return wah; // wah is iterator
}
let bar = {} // bar is iterable
bar[Symbol.iterator] = foo;
console.log([...bar]); // [1, 3, 5]
for (a of bar) console.log(a); // 1 3 5 (in three lines)
Entonces, en el código anterior, bar
es el iterable, y wah
es el iterador, y next()
es la interfaz del iterador.
Entonces, iterable e iterator son cosas diferentes.
Ahora, sin embargo, en un ejemplo común de generador e iterador:
function* gen1() {
yield 1;
yield 3;
yield 5;
}
const iter1 = gen1();
console.log([...iter1]); // [1, 3, 5]
for (a of iter1) console.log(a); // nothing
const iter2 = gen1();
for (a of iter2) console.log(a); // 1 3 5 (in three lines)
console.log(iter1[Symbol.iterator]() === iter1); // true
En el caso anterior, gen1
es el generador, y iter1
es el iterador, y iter1.next()
hará el trabajo adecuado. Pero iter1[Symbol.iterator]
da una función que, cuando se invoca, devuelve iter1
, que es un iterador. Entonces, ¿ iter1
es tanto iterable como iterador en este caso?
Además, iter1
es diferente del ejemplo 1 anterior, porque el iterable en el ejemplo 1 puede dar [1, 3, 5]
tantas veces como quiera usando [...bar]
, mientras que iter1
es iterable, pero dado que regresa a sí mismo, que es el mismo iterador cada vez, solo dará [1, 3, 5]
una vez.
Entonces, podemos decir, para un iterable bar
, cuántas veces puede [...bar]
dar el resultado [1, 3, 5]
, y la respuesta es que depende. ¿Y es iterable lo mismo que un iterador? Y la respuesta es que son cosas diferentes, pero pueden ser lo mismo, cuando el iterable se utiliza como iterador. ¿Es eso correcto?
fuente
iter1
es tanto iterable como iterador en este caso? ", Sí. Todos los iteradores nativos también son iterables devolviéndose ellos mismos, para que pueda pasarlos fácilmente a construcciones que esperan un iterable.Respuestas:
Sí, iterables y iteradores son cosas diferentes, pero la mayoría de los iteradores (incluyendo todos los que te dan de sí misma JavaScript, como de los
keys
ovalues
métodos enArray.prototype
o generadores de funciones del generador) heredan de la % objeto% IteratorPrototype , que cuenta con unSymbol.iterator
método como esta:El resultado es que todos los iteradores estándar también son iterables. Eso es para que pueda usarlos directamente, o usarlos en
for-of
bucles y demás (que esperan iterables, no iteradores).Considere el
keys
método de matrices: devuelve un iterador de matriz que visita las claves de la matriz (sus índices, como números). Tenga en cuenta que devuelve un iterador . Pero un uso común es:for-of
toma un iterable , no un iterador , entonces, ¿por qué funciona eso?Funciona porque el iterador también es iterable;
Symbol.iterator
solo vuelvethis
.Aquí hay un ejemplo que uso en el Capítulo 6 de mi libro: si desea recorrer todas las entradas pero omite la primera y no desea utilizar
slice
para cortar el subconjunto, puede obtener el iterador, leer el primer valor, luego pasar a unfor-of
bucle:Tenga en cuenta que esto es todo iteradores estándar . Algunas veces, las personas muestran ejemplos de iteradores codificados manualmente como este:
Mostrar fragmento de código
El iterador devuelto por
range
allí no es iterable, por lo que falla cuando intentamos usarlo confor-of
.Para hacerlo iterable, tendríamos que:
Symbol.iterator
método al comienzo de la respuesta anterior, oLamentablemente, TC39 decidió no proporcionar una forma directa de obtener el objeto% IteratorPrototype%. Hay una forma indirecta (obtener un iterador de una matriz, luego tomar su prototipo, que se define como% IteratorPrototype%), pero es una molestia.
Pero de todos modos no hay necesidad de escribir iteradores de forma manual; solo use una función de generador, ya que el generador que devuelve es iterable:
Mostrar fragmento de código
En contraste, no todos los iterables son iteradores. Las matrices son iterables, pero no iteradores. También lo son las cadenas, los mapas y los conjuntos.
fuente
Descubrí que hay algunas definiciones más precisas de los términos, y estas son las respuestas más definitivas:
De acuerdo con las especificaciones ES6 y MDN :
Cuando nosotros tenemos
foo
se llama función generadora . Y luego cuando tenemosbar
Es un objeto generador . Y un objeto generador se ajusta tanto al protocolo iterable como al protocolo iterador .La versión más simple es la interfaz iteradora, que es solo un
.next()
método.El protocolo iterable es: para el objeto
obj
,obj[Symbol.iterator]
proporciona una "función de argumentos cero que devuelve un objeto, conforme al protocolo iterador".Por el título del enlace MDN , también parece que también podemos llamar a un objeto generador un "generador".
Tenga en cuenta que en el libro de Nicolas Zakas, Understanding ECMAScript 6 , probablemente llamó libremente a una "función generadora" como "generador" y a un "objeto generador" como "iterador". El punto de partida es que ambos están realmente relacionados con el "generador": uno es una función generadora y el otro es un objeto generador o generador. El objeto generador se ajusta tanto al protocolo iterable como al protocolo iterador.
Si es solo un objeto conforme al protocolo iterador , no puede usar
[...iter]
ofor (a of iter)
. Tiene que ser un objeto que se ajuste al protocolo iterable .Y luego, también hay una nueva clase Iterator, en una futura especificación de JavaScript que todavía está en borrador . Tiene una interfaz más grande, incluyendo métodos tales como
forEach
,map
,reduce
de la interfaz de matriz actual, y los nuevos, tales como ytake
, ydrop
. El iterador actual se refiere al objeto solo con lanext
interfaz.Para responder a la pregunta original: cuál es la diferencia entre un iterador y un iterable, la respuesta es: un iterador es un objeto con la interfaz
.next()
, y un iterable es un objetoobj
queobj[Symbol.iterator]
puede dar una función de argumento cero que, cuando se invoca, devuelve un iteradorY un generador es tanto iterable como iterador, para agregar a eso.
fuente