Iterables en JavaScript

El protocolo de iteraciones permite a los objetos de JavaScript definir y personalizar su comportamiento de iteración, como los valores que se recorren en for...of.

Se considera a los arreglos como iterables. Pero, también existen otros iterables (por ejemplo, cadenas).

Describiendo a Symbol.iterator

Vamos a intentar crear un iterable.

Por ejemplo, hay un objeto, que no es un arreglo pero puede ser adecuado para for...of.

Veamos un ejemplo de un objeto de rango, que representa un intervalo de números:

let range = {
  from: 0,
  to: 10
}; 
// We want the for..of to work:
// for(let num of range) ... num=0,1,2,3,4,5,6,7,8,9,10

Para transformar el rango en iterable, es necesario agregar un método al objeto llamado Symbol.iterator.

Estos son los pasos:

  1. Una vez que for..of comienza, se llama al método una vez. Debe devolver un iterador.
  2. Entonces, for..of puede operar con el objeto devuelto.
  3. Una vez que, for..of espera el siguiente valor, se llama a next() en ese objeto.
  4. Su resultado debería incluir la forma {done: Boolean, value: any} .

El rendimiento completo para el rango con observaciones se verá de la siguiente manera:

Javascript range object call Symbol.iterator
let range = { from: 0, to: 5 }; // 1. called to for..of initially calls this range[Symbol.iterator] = function () { //returns an iterator object: // 2.for..of works only with this iterator, querying it for next values return { currentValue: this.from, lastValue: this.to, // 3. next() is called at each iteration with a for..of loop next() { // 4. it should return the value as an object {done:.., value :...} if (this.currentValue <= this.lastValue) { return { done: false, value: this.currentValue++ }; } else { return { done: true }; } } }; }; for (let num of range) { console.log(num); // 0, then 1, 2, 3, 4, 5 }

La característica más esencial de los iterables es la segregación de las preocupaciones. Considera que el objeto iterador es distinto de los que itera. Es posible fusionarlos usando el rango como un iterador para simplificar el código, como se muestra a continuación:

Javascript range object call Symbol.iterator
let range = { from: 0, to: 5, [Symbol.iterator]() { this.currentValue = this.from; return this; }, next() { if (this.currentValue <= this.to) { return { done: false, value: this.currentValue++ }; } else { return { done: true }; } } }; for (let num of range) { console.log(num); // 0, then 1, 2, 3, 4, 5 }

La principal desventaja de este caso es que no es posible tener dos for..of que se ejecuten sobre el objeto al mismo tiempo.

Pueden haber iteradores infinitos. Por ejemplo, puedes transformar el rango en infinito con range.to = Infinity. También hay otra opción: puedes crear un objeto iterable, que genera una secuencia infinita de números pseudoaleatorios. Entonces, no se aplican limitaciones a next, y puede devolver más valores. En consecuencia, el bucle for..of también se volverá interminable en un iterable como este. Pero puede detenerse con break.

Las cadenas como iterables

Los iterables más utilizados son las cadenas y los arreglos.

El for..of puede recorrer sus caracteres, en caso de una cadena:

Javascript strings iterables
for (let char of "W3Docs") { // triggers 6 times: once for each character console.log(char); // W, then 3, then D, then o, then c, then s }

Funcionará correctamente, de esta manera:

Javascript strings iterables
let str = 'ȬẂ'; for (let char of str) { console.log(char); // Ȭ, and then Ẃ }

Arreglo-como y Iterables

En este párrafo, revisaremos los términos iterables y arreglo-como. Pueden parecer similares pero son muy diferentes.

Se considera que los arreglo-como son objetos que contienen índices y length. Los iterables son objetos que son capaces de implementar el método Symbol.iterator . Este método se describió anteriormente.

Al usar JavaScript, puedes encontrar muchos objetos de arreglo-como e iterable. Por ejemplo, las cadenas pueden ser tanto iterables como arreglo-como. Por el contrario, un iterable nunca puede convertirse en un arreglo-como, y viceversa.

Vamos a ver un ejemplo de un objeto que es un arreglo-como pero no un iterable:

Javascript object is an array-like not an iterable
let arrLike = { // has indexes and length => array-like 0: "Welcome", 1: "to", 2: "W3Docs", length: 3 }; // Error (no Symbol.iterator) for (let item of arrLike) { console.log(item); }

Por regla general, ni los iterables ni los arreglo-como son arreglos, ya que no contienen pop, push, etc.

Describiendo Array-form

Array-form es un método genérico que se utiliza para tomar un valor iterable o arreglo-como y convertirlo en un verdadero arreglo. Luego, se pueden llamar a los métodos de arreglo en él. Aquí hay un ejemplo de cómo usar array-form:

Javascript object is an array-like iterable
let arrLike = { 0: "Welcome", 1: "to", 2: "W3Docs", length: 3 }; let arr = Array.from(arrLike); // (*) console.log(arr.pop()); // W3Docs (method operates)

En la línea (*), Array.from recibe el objeto, examinando si es un arreglo-como o un iterable, luego crea un nuevo arreglo, copiando todos los elementos en él. Veamos cómo sucede lo mismo con un iterable:

Javascript array an iterable
let range = { from: 0, to: 5, [Symbol.iterator]() { this.currentValue = this.from; return this; }, next() { if (this.currentValue <= this.to) { return { done: false, value: this.currentValue++ }; } else { return { done: true }; } } }; //confirming that the range is obtained from the above example let arr = Array.from(range); console.log(arr); // 0,1,2,3,4,5 ;array toString conversion works

La sintaxis completa de Array.from se ve así:

Array.from(obj[, mapFunc, thisArg])

El segundo argumento mapFn es una función que puede ser aplicada a cada elemento antes de añadirlo al arreglo. El tercer argumento thisArg permite establecer this para ello. Aquí está un ejemplo:

Javascript object is an array-like not an iterable
let range = { from: 0, to: 5, [Symbol.iterator]() { this.currentValue = this.from; return this; }, next() { if (this.currentValue <= this.to) { return { done: false, value: this.currentValue++ }; } else { return { done: true }; } } }; // provided that the range is taken from the above example // square of each number let arr = Array.from(range, num => num * num); console.log(arr); // 0,1,4,9,16,25

Ahora, convirtamos una cadena en un arreglo de caracteres con la ayuda de Array.from:

Javascript turn a string into an array of characters
let str = 'ȬẂ'; // splits the str into an array of characters let chars = Array.from(str); console.log(chars[0]); // Ȭ console.log(chars[1]); // Ẃ console.log(chars.length); // 2

A diferencia del método str.split, se basa en la naturaleza iterable de la cadena.

Actúa de la misma manera que:

Javascript turn a string into an array of characters
let str = 'ȬẂ'; let charsArr = []; // Array.from internally does the same loop for (let char of str) { charsArr.push(char); } console.log(charsArr);

Resumen

Los iterables son los objetos que pueden usarse en el for..of.

Como regla general, deberían ejecutar el método Symbol.iterator. Este método, generalmente, es llamado automáticamente por for..of. Pero, también se puede hacer directamente. Los objetos que tienen propiedades indexadas y length se consideran arreglo-como. También pueden incluir otras propiedades y métodos, pero no son arreglos reales. Para transformarlos en arreglos, puedes usar el método Array.from(obj[, mapFn, thisArg]).

Los argumentos mapFn y thisArg permiten aplicar una función a cada uno de ellos.

Hora del Cuestionario: ¡Pon a Prueba Tus Habilidades!

¿Listo para desafiar lo que has aprendido? Sumérgete en nuestros cuestionarios interactivos para una comprensión más profunda y una forma divertida de reforzar tu conocimiento.

¿Te resulta útil?