ES6 - Iterador
Introducción a Iterator
El iterador es un objeto que nos permite acceder a una colección de objetos de uno en uno.
Los siguientes tipos integrados son iterables por defecto:
- String
- Array
- Map
- Set
Se considera un objeto iterable, si el objeto implementa una función cuya clave es [Symbol.iterator]y devuelve un iterador. Un bucle for ... of se puede utilizar para iterar una colección.
Ejemplo
El siguiente ejemplo declara una matriz, marca e itera a través de ella usando un for..of lazo.
<script>
let marks = [10,20,30]
//check iterable using for..of
for(let m of marks){
console.log(m);
}
</script>
La salida del código anterior será como se indica a continuación:
10
20
30
Ejemplo
El siguiente ejemplo declara una matriz, marca y recupera un objeto iterador. los[Symbol.iterator]()se puede utilizar para recuperar un objeto iterador. El método next () del iterador devuelve un objeto con'value' y 'done'propiedades. 'done' es booleano y devuelve verdadero después de leer todos los elementos de la colección.
<script>
let marks = [10,20,30]
let iter = marks[Symbol.iterator]();
console.log(iter.next())
console.log(iter.next())
console.log(iter.next())
console.log(iter.next())
</script>
La salida del código anterior será como se muestra a continuación:
{value: 10, done: false}
{value: 20, done: false}
{value: 30, done: false}
{value: undefined, done: true}
Iterable personalizado
Ciertos tipos en JavaScript son iterables (por ejemplo, matriz, mapa, etc.) mientras que otros no lo son (por ejemplo, clase). Los tipos de JavaScript que no son iterables de forma predeterminada se pueden iterar utilizando el protocolo iterable.
El siguiente ejemplo define una clase llamada CustomerListque almacena varios objetos de cliente como una matriz. Cada objeto de cliente tiene propiedades firstName y lastName.
Para que esta clase sea iterable, la clase debe implementar [Symbol.iterator]()función. Esta función devuelve un objeto iterador. El objeto iterador tiene una funciónnext que devuelve un objeto {value:'customer',done:true/false}.
<script>
//user defined iterable
class CustomerList {
constructor(customers){
//adding customer objects to an array
this.customers = [].concat(customers)
}
//implement iterator function
[Symbol.iterator](){
let count=0;
let customers = this.customers
return {
next:function(){
//retrieving a customer object from the array
let customerVal = customers[count];
count+=1;
if(count<=customers.length){
return {
value:customerVal,
done:false
}
}
//return true if all customer objects are iterated
return {done:true}
}
}
}
}
//create customer objects
let c1={
firstName:'Sachin',
lastName:'Tendulkar'
}
let c2={
firstName:'Rahul',
lastName:'Dravid'
}
//define a customer array and initialize it let customers=[c1,c2]
//pass customers to the class' constructor
let customersObj = new CustomerList(customers);
//iterating using for..of
for(let c of customersObj){
console.log(c)
}
//iterating using the next() method
let iter = customersObj[Symbol.iterator]();
console.log(iter.next())
console.log(iter.next())
console.log(iter.next())
</script>
La salida del código anterior será la siguiente:
{firstName: "Sachin", lastName: "Tendulkar"}
{firstName: "Rahul", lastName: "Dravid"}
{
done: false
value: {
firstName: "Sachin",
lastName: "Tendulkar"
}
}
{
done: false
value: {
firstName: "Rahul",
lastName: "Dravid"
}
}
{done: true}
Generador
Antes de ES6, las funciones en JavaScript seguían un modelo de ejecución hasta su finalización. ES6 introduce funciones conocidas como Generador que pueden detenerse a mitad de camino y luego continuar desde donde se detuvo.
Un generador antepone el nombre de la función con un carácter de asterisco * y contiene uno o más yielddeclaraciones. losyield La palabra clave devuelve un objeto iterador.
Sintaxis
function * generator_name() {
yield value1
...
yield valueN
}
Ejemplo
El ejemplo define una función de generador getMarkscon tres declaraciones de rendimiento. A diferencia de las funciones normales, elgenerator function getMarks(), cuando se invoca, no ejecuta la función pero devuelve un objeto iterador que le ayuda a ejecutar código dentro de la función generadora.
En la primera llamada a markIter.next()las operaciones al principio se ejecutarían y la declaración de rendimiento pausa la ejecución del generador. Las siguientes llamadas almarkIter.next() reanudará la función del generador hasta el próximo yield expresión.
<script>
//define generator function
function * getMarks(){
console.log("Step 1")
yield 10
console.log("Step 2")
yield 20
console.log("Step 3")
yield 30
console.log("End of function")
}
//return an iterator object
let markIter = getMarks()
//invoke statements until first yield
console.log(markIter.next())
//resume execution after the last yield until second yield expression
console.log(markIter.next())
//resume execution after last yield until third yield expression
console.log(markIter.next())
console.log(markIter.next()) // iteration is completed;no value is returned
</script>
La salida del código anterior será la que se menciona a continuación:
Step 1
{value: 10, done: false}
Step 2
{value: 20, done: false}
Step 3
{value: 30, done: false}
End of function
{value: undefined, done: true}
Ejemplo
El siguiente ejemplo crea una secuencia infinita de números pares hasta
* Función de generador evenNumberGenerator.
Podemos iterar a través de todos los números pares usando next() o usando for of bucle como se muestra a continuación
<script>
function * evenNumberGenerator(){
let num = 0;
while(true){
num+=2
yield num
}
}
// display first two elements
let iter = evenNumberGenerator();
console.log(iter.next())
console.log(iter.next())
//using for of to iterate till 12
for(let n of evenNumberGenerator()){
if(n==12)break;
console.log(n);
}
</script>
La salida del código anterior será la siguiente:
{value: 2, done: false}
{value: 4, done: false}
2
4
6
8
10