Symbol.iterator: Implementa el Protocolo de Iteración
Aprende cómo hacer tus objetos iterables usando Symbol.iterator para usarlos con for...of, spread operator y destructuring.
TL;DR - Resumen rápido
- Symbol.iterator es un well-known symbol que define cómo un objeto es iterable
- Un objeto iterable debe tener un método con clave Symbol.iterator que retorna un iterador
- Un iterador es un objeto con método next() que retorna {value, done}
- Los objetos iterables funcionan con for...of, spread operator y destructuring
- Los generadores simplifican la implementación de iteradores usando function* y yield
Introducción a Symbol.iterator
Symbol.iterator es un well-known symbol que te permite hacer que tus objetos sean iterables. Un objeto iterable es aquel que puede ser recorrido secuencialmente usando estructuras como for...of, el spread operator (...) o destructuring de arrays. Este símbolo es fundamental para trabajar con colecciones personalizadas de forma nativa en JavaScript.
Antes de ES6, las estructuras de datos personalizadas no podían ser iteradas de forma estándar. Tenías que implementar métodos personalizados o convertir los datos a arrays antes de iterar. Symbol.iterator resuelve este problema proporcionando un protocolo estandarizado que JavaScript reconoce y utiliza automáticamente.
Objetos nativos iterables
Muchos objetos nativos de JavaScript ya son iterables por defecto: Array, String, Map, Set, TypedArray, arguments y NodeList. Estos objetos implementan Symbol.iterator internamente, lo que permite usarlos directamente con for...of y spread operator.
¿Qué es Symbol.iterator?
Symbol.iterator es una propiedad estática del objeto Symbol que representa un valor único. Cuando un objeto tiene una propiedad con este símbolo como clave, JavaScript sabe que el objeto es iterable y puede ser recorrido usando el protocolo de iteración.
El valor de Symbol.iterator debe ser una función que retorna un iterador. Un iterador es un objeto con un método next() que, cada vez que se llama, retorna un objeto con dos propiedades: value (el valor actual) y done (un booleano que indica si la iteración ha terminado).
Este ejemplo muestra cómo verificar si un objeto es iterable accediendo a Symbol.iterator. Los objetos nativos como Array y String tienen esta propiedad implementada, mientras que los objetos regulares no. Para hacer un objeto iterable, debes implementar tu propio método Symbol.iterator.
- Symbol.iterator es un well-known symbol que identifica objetos iterables
- El valor de Symbol.iterator debe ser una función que retorna un iterador
- Un iterador es un objeto con método next() que retorna {value, done}
- JavaScript busca Symbol.iterator automáticamente al usar for...of
- Los objetos regulares no son iterables por defecto
Objetos regulares no son iterables
Los objetos literales no son iterables por defecto. Intentar iterar sobre ellos con for...of lanzará un error. Si necesitas iterar sobre las propiedades de un objeto, usa Object.keys(), Object.values(), Object.entries() o for...in.
Protocolo de Iteración
El protocolo de iteración define cómo los objetos deben comportarse para ser considerados iterables. Este protocolo consta de dos partes: el protocolo iterable y el protocolo iterador. Entender ambos es fundamental para implementar correctamente Symbol.iterator.
El protocolo iterable requiere que el objeto tenga un método con clave Symbol.iterator que no reciba argumentos y retorne un iterador. El protocolo iterador requiere que el objeto retornado tenga un método next() que retorne un objeto con las propiedades value y done.
Este ejemplo muestra cómo funciona el protocolo de iteración manualmente. Al llamar al método Symbol.iterator de un array, obtenemos un iterador. Luego, llamamos a next() repetidamente hasta que done es true. Este es exactamente lo que hace for...of internamente.
El protocolo es estándar
El protocolo de iteración es un estándar de JavaScript. Cualquier objeto que lo implemente correctamente será iterable, independientemente de cómo esté implementado internamente. Esto permite crear colecciones personalizadas que se comportan como arrays nativos.
Implementar Symbol.iterator
Para hacer un objeto iterable, debes implementar una propiedad con clave Symbol.iterator. Esta propiedad debe ser una función que retorne un iterador. El iterador debe tener un método next() que retorne un objeto con value y done.
La implementación más común es usar un generador (function*), que simplifica la creación de iteradores. Los generadores automáticamente crean un iterador con el método next() implementado, usando yield para retornar cada valor.
Este ejemplo muestra cómo implementar Symbol.iterator usando un generador. La clase MiRango implementa Symbol.iterator como un generador que yieldea valores desde inicio hasta fin. Esto permite usar la instancia con for...of, spread operator y destructuring. El segundo ejemplo muestra que también puedes implementar Symbol.iterator en objetos literales.
- Usa function* para crear un generador que simplifica la implementación
- Usa yield para retornar cada valor de la iteración
- El generador automáticamente crea un iterador con método next()
- Puedes implementar Symbol.iterator en clases o objetos literales
- Los generadores manejan automáticamente el estado de la iteración
No olvides retornar el iterador
Si implementas Symbol.iterator sin usar generadores, asegúrate de retornar un objeto con método next(). Si no lo haces, JavaScript lanzará un error al intentar iterar. Los generadores automáticamente retornan el iterador correcto.
Usos Prácticos
Symbol.iterator tiene muchos usos prácticos en JavaScript moderno. Los más comunes incluyen crear colecciones personalizadas, iterar sobre estructuras de datos complejas y simplificar el código usando for...of en lugar de bucles tradicionales.
Cuando implementas Symbol.iterator en tus objetos, puedes usarlos con todas las características de JavaScript que esperan iterables. Esto incluye for...of, spread operator, destructuring, Array.from() y métodos como Promise.all().
Este ejemplo muestra una colección personalizada que implementa Symbol.iterator. La clase ListaEnlazada permite iterar sobre sus nodos usando for...of, convertir a array con spread operator y usar destructuring. Esto hace que la colección sea mucho más útil y fácil de usar.
Iterables asíncronos
Además de Symbol.iterator, JavaScript tiene Symbol.asyncIterator para iterables asíncronos. Los iterables asíncronos funcionan con for await...of y son útiles para iterar sobre streams de datos asíncronos como resultados de fetch() o lecturas de archivos.
Resumen: Symbol.iterator
Conceptos principales:
- •Symbol.iterator es un well-known symbol que define objetos iterables
- •Un iterable debe tener un método Symbol.iterator que retorna un iterador
- •Un iterador tiene método next() que retorna {value, done}
- •Los generadores (function*) simplifican la implementación de iteradores
- •Los iterables funcionan con for...of, spread operator y destructuring
Mejores prácticas:
- •Usa generadores (function*) para implementar Symbol.iterator de forma sencilla
- •Implementa Symbol.iterator en colecciones personalizadas para mejor UX
- •Asegúrate de que next() retorne siempre {value, done}
- •Usa iterables en lugar de arrays cuando sea posible para lazy evaluation
- •Considera Symbol.asyncIterator para iterables asíncronos