EcmaScript 6

En ocasiones tenemos algún tipo de lista de la que queremos recorrer su elementos de uno en uno. Para este menester vienen a ayudarnos nuestros amigos los iteradores. Y es que en JavaScript podemos hacer que cualquier objeto funcione como un iterador, de forma que podemos ir moviéndonos uno a uno por los elementos que lo componen.

Lo primero de todo es definir como es la estructura de un iterador en JavaScript, y es que es tan simple, como un objeto que tiene definido un método next() y devuelve un objeto con dos atributos:

  • value: valor actual en la secuencia.
  • done: valor booleano que será true cuando hemos terminado de recorrer la secuencia y false mientras podamos seguir recorriéndola.

Como ejemplo para entender este concepto, se va a partir de una lista enlazada, en la que se podrá ir recorriendo sus elementos uno a uno.

// Enlace de la lista, el código es muy sencillo por lo que no requeriría de más explicación.
class Link {
  #name;
  #next;

  constructor(name, next = null) {
    this.#name = name;
    this.#next = next;
  }

  get Name() {
    return this.#name;
  }

  get Next() {
    return this.#next;
  }
}

// Clase que será iterable y que contendrá la lista de enlaces. Esta clase es sólo un ejemplo, por eso es tan sencilla.
class Chain {
  #firstLink;

  constructor(firstLink) {
    this.#firstLink = firstLink;
  }

  // La parte importante está aquí, ya que es lo que permite que los objetos de esta clase se puedan iterar.
  [Symbol.iterator]() {
    // Apuntamos al primer elemento de la lista.
    let nextLink = this.#firstLink;

    // Aunque ya lo indicamos arriba, en cada iteración hay que devolver un objeto que contenga el método next(). Dicho
    // método es el que devolverá a su vez devolverá el objeto con los atributos "done" y "value".
    return {
      next() {
        const result = {
          done: nextLink === null,
          value: nextLink
        };

        if (nextLink) {
          nextLink = nextLink.Next;
        }

        return result;
      }
    }
  }
}

En el código he ido incluyendo comentarios con la explicación. Pero básicamente para que los objetos de nuestra clase se puedan iterar, necesitamos escribir un método cuyo nombre sea [Symbol.iterator](). Con esto se indica a JavaScript que el objeto es iterable.

Ahora que tenemos construida la definición vamos a ver lo que ocurriría al incluir el objeto en un bucle for...of:

// Creamos tres enlaces, quedando el último de ellos al final de la lista.
const firstLink = new Link("first", new Link("second", new Link("third")));
const chain = new Chain(firstLink);

for (const link of chain) {
  console.log(link.Name);
}

// Lo anterior escribe por consola:
// first
// second
// third

// También es posible llamar directamente al iterador, con una sintaxis un tanto fea:
const iterator = chain[Symbol.iterator]();
it.next(); // { done: false, value: { #name: "first", ... }
it.next(); // { done: false, value: { #name: "second", ... }
it.next(); // { done: false, value: { #name: "third", ... }
it.next(); // { done: true, value: null }

Como se puede ver, en cada vuelta del bucle for...of, obtenemos el atributo value que devolvimos con el método next(). Tras esto, si volvemos a escribir un bucle con nuestro objeto, se comenzará de nuevo el recorrido desde el primer elemento de la lista.

Finalmente, se podría dar el caso de que necesitásemos convertir el iterador en un array. Pues bien, para eso podemos utilizar el método Array.from().

let values = Array.from(chain);
// Ahora values contiene:
// [
//   0: {
//     #name: "first",
//     #next: Link a "second"
//   },
//   1: {
//     #name: "second",
//     #next: Link a "third" 
//   },
//   2: {
//     #name: "third",
//     #next: null
//   }
// ]

En la segunda y última parte de esta mini-guía veremos lo que son las funciones generadoras.

Comparte este artículo con quien quieras
Receta: Validar valores numéricos en JavaScript
Iteradores en JavaScript. Parte 2. Funciones generadoras

Leave a Comment

Your email address will not be published. Required fields are marked *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.