En JavaScript, los iterables son objetos que nos permiten recorrer y acceder a los elementos de una colección de datos de manera secuencial y sencilla.
En términos simples, iterables e iteradores son una herramienta que nos permite “iterar” sobre los valores de una colección.
A diferencia de los métodos tradicionales de acceso directo a los elementos (como los índices en los arrays), los iteradores permiten recorrer colecciones sin necesidad de tener acceso directo a la estructura interna.
Los iterables pueden ser recorridos de manera secuencial utilizando un bucle for...of
u otras funciones que trabajan con iterables, como map()
, filter()
, reduce()
, entre otros.
Como funcionan los iterables
Bajo el capó, un iterable es cualquier objeto que implementa el método Symbol.iterator
, el cual devuelve un iterador.
Por su parte, un iterador es simplemente un objeto que implementa un método next()
. Este método devuelve un objeto con dos propiedades:
value
: El valor actual de la iteración.done
: Un valor booleano (true
ofalse
) que indica si la iteración ha finalizado.
Por ejemplo, al usar un iterador para recorrer un array, cada llamada a next()
devolverá el siguiente valor del array hasta que se alcance el final.
En resumen,
- Iterable: Un objeto que tiene un método
Symbol.iterator
que devuelve un iterador. - Iterador: Un objeto con el método
next()
que devuelve un objeto con las propiedadesvalue
ydone
.
Usando iteradores con bucles
Iteración manual con next()
Para utilizar un iterador, podemos llamarlo manualmente mediante el método next()
.
const iterable = [10, 20, 30];
const iterator = iterable[Symbol.iterator]();
let result = iterator.next();
while (!result.done) {
console.log(result.value); // Imprime los valores 10, 20, 30
result = iterator.next();
}
En este ejemplo,
- Comenzamos la iteración con
iterator.next()
- Luego usamos un bucle
while
para continuar iterando hasta que se marque la propiedaddone
comotrue
.
Uso de for...of
Una forma más sencilla y común de trabajar con iteradores es mediante el bucle for...of
, que automáticamente maneja el proceso de iteración.
const iterable = [1, 2, 3, 4];
for (const value of iterable) {
console.log(value); // Imprime 1, 2, 3, 4
}
- Este bucle se encargará de llamar a
next()
repetidamente hasta que se haya recorrido todo el iterable - Internamente, el bucle utiliza el método
next()
del iterador.
Crear nuestros propios iteradores e iterables
También podemos crear nuestros propios iterables e iteradores. Esto es útil cuando necesitamos una forma personalizada de iterar sobre una colección de datos que no es estándar, o cuando estamos trabajando con estructuras de datos complejas.
Iterador custom
Por ejemplo, imaginemos que queremos crear un iterador para recorrer un objeto que contiene una lista de valores. Podemos hacerlo implementando el método next()
dentro de un objeto:
const miIterador = {
valores: [10, 20, 30],
currentIndex: 0,
next() {
if (this.currentIndex < this.valores.length) {
return { value: this.valores[this.currentIndex++], done: false };
}
return { value: undefined, done: true };
}
};
console.log(miIterador.next()); // { value: 10, done: false }
console.log(miIterador.next()); // { value: 20, done: false }
console.log(miIterador.next()); // { value: 30, done: false }
console.log(miIterador.next()); // { value: undefined, done: true }
En este ejemplo,
- Hemos creado un objeto
miIterador
que actúa como un iterador para un array de números. - La propiedad
currentIndex
lleva un seguimiento del índice actual en el array - El método
next()
devuelve el siguiente valor en la secuencia hasta que se alcance el final.
Iterable custom
Para crear un objeto iterable, debemos implementar el método Symbol.iterator
, que es un tipo de propiedad de símbolo en JavaScript.
class CustomCollection {
constructor(...items) {
this.items = items;
}
[Symbol.iterator]() {
let index = 0;
const items = this.items;
return {
next() {
if (index < items.length) {
return { value: items[index++], done: false };
} else {
return { value: undefined, done: true };
}
}
};
}
}
const collection = new CustomCollection('x', 'y', 'z');
for (const item of collection) {
console.log(item);
}
// Salida:
// x
// y
// z
En este ejemplo,
CustomCollection
es un objeto que implementa el métodoSymbol.iterator
- Este devuelve un iterador con el método
next()
que retorna los valores de la propiedaditems
uno por uno hasta que se alcanza el final de la secuencia.
Utilizando generadores como iterables
También podemos utilizar generadores para crear objetos iterables de una manera más concisa y legible.
function* miGenerador() {
yield 'Hola';
yield 'Mundo';
}
const iterador = miGenerador();
for (const valor of iterador) {
console.log(valor);
}
En este ejemplo, el generador miGenerador
crea un objeto iterable que genera los valores “Hola” y “Mundo” cuando es recorrido con un bucle for...of
.
Si quieres saber más sobre funciones generadoras, consulta la entrada