Iteradores y generadores ES6

    Los iteradores y generadores suelen ser un pensamiento secundario al escribir c贸digo, pero si puede tomar unos minutos para pensar en c贸mo usarlos para simplificar su c贸digo, le ahorrar谩n mucha depuraci贸n y complejidad. Con los nuevos iteradores y generadores de ES6, JavaScript obtiene una funcionalidad similar a Iterable de Java, lo que nos permite personalizar nuestra iteraci贸n en objetos.

    Por ejemplo, si tiene un objeto Graph, puede usar f谩cilmente un generador para recorrer los nodes o bordes. Esto hace que el c贸digo sea mucho m谩s limpio al colocar la l贸gica transversal dentro del objeto Graph donde pertenece. Esta separaci贸n de la l贸gica es una buena pr谩ctica, y los iteradores / generadores facilitan el seguimiento de estas mejores pr谩cticas.

    Iteradores y generadores ES6

    Iteradores

    Usando iteradores, puede crear una forma de iterar usando el for...of construir para su objeto personalizado. En lugar de usar for...in, que solo recorre todas las propiedades del objeto, utilizando for...of nos permite hacer un iterador mucho m谩s personalizado y estructurado en el que elegimos qu茅 valores devolver para cada iteraci贸n.

    Bajo el cap贸, for...of en realidad est谩 usando Symbol.iterator. Los s铆mbolos de recuperaci贸n son una nueva caracter铆stica de JavaScript ES6. los Symbol.iterator es un s铆mbolo de prop贸sito especial creado especialmente para acceder al iterador interno de un objeto. Entonces, podr铆a usarlo para recuperar una funci贸n que itera sobre un objeto de matriz, as铆:

    var nums = [6, 7, 8];
    var iterator = nums[Symbol.iterator]();
    iterator.next();				// Returns { value: 6, done: false }
    iterator.next();				// Returns { value: 7, done: false }
    iterator.next();				// Returns { value: 8, done: false }
    iterator.next();				// Returns { value: undefined, done: true }
    

    Cuando usa el for...of constructo, esto es en realidad lo que se utiliza debajo. Observe c贸mo se devuelve cada valor posterior, junto con un indicador que le indica si est谩 en el La gran mayor铆a de las veces no necesitar谩 usar next() manualmente as铆, pero la opci贸n est谩 ah铆 en caso de que tenga un caso de uso que requiera un bucle m谩s complejo.

    Puedes usar Symbol.iterator para definir la iteraci贸n especializada para un objeto. Entonces, digamos que tiene su propio objeto que es un envoltorio para oraciones, Sentence.

    function Sentence(str) {
        this._str = str;
    }
    

    Para definir c贸mo iteramos sobre las partes internas del objeto Sentence, proporcionamos una funci贸n de iterador prototipo:

    Sentence.prototype[Symbol.iterator] = function() {
        var re = /S+/g;
        var str = this._str;
    
        return {
            next: function() {
                var match = re.exec(str);
                if (match) {
                    return {value: match[0], done: false};
                }
                return {value: undefined, done: true};
            }
        }
    };
    

    Ahora, utilizando el iterador que acabamos de crear anteriormente (que contiene una cadena de expresiones regulares que coincide solo con palabras), podemos iterar f谩cilmente sobre las palabras de cualquier oraci贸n que proporcionemos:

    var s = new Sentence('Good day, kind sir.');
    
    for (var w of s) {
        console.log(w);
    }
    
    // Prints:
    // Good
    // day,
    // kind
    // sir.
    

    Generadores

    Los generadores ES6 se basan en lo que proporcionan los iteradores mediante el uso de una sintaxis especial para crear m谩s f谩cilmente la funci贸n de iteraci贸n. Los generadores se definen utilizando el function* palabra clave. Dentro de una function*, puede devolver valores repetidamente usando yield. los yield La palabra clave se utiliza en las funciones del generador para pausar la ejecuci贸n y devolver un valor. Se puede considerar como una versi贸n basada en generador del return palabra clave. En la siguiente iteraci贸n, la ejecuci贸n se reanudar谩 en el 煤ltimo punto que yield se utiliz贸.

    function* myGenerator() {
        yield 'foo';
        yield 'bar';
        yield 'baz';
    }
    
    myGenerator.next();		// Returns {value: 'foo', done: false}
    myGenerator.next();		// Returns {value: 'bar', done: false}
    myGenerator.next();		// Returns {value: 'baz', done: false}
    myGenerator.next();		// Returns {value: undefined, done: true}
    

    O puede utilizar el for...of construir:

    for (var n of myGenerator()) {
        console.log(n);
    }
    
    // Prints
    // foo
    // bar
    // baz
    

    En este caso, podemos ver que el Generador luego se encarga de devolver {value: val, done: bool} objeto para usted, que se maneja bajo el cap贸 en for...of.

    Entonces, 驴c贸mo podemos utilizar los generadores a nuestro favor? Volviendo a nuestro ejemplo anterior, podemos simplificar el Sentence iterador al siguiente c贸digo:

    Sentence.prototype[Symbol.iterator] = function*() {
        var re = /S+/g;
        var str = this._str;
        var match;
        while (match = re.exec(str)) {
            yield match[0];
        }
    };
    

    Observe c贸mo la funci贸n del iterador (ahora un generador) es mucho m谩s peque帽a que la versi贸n anterior. Ya no necesitamos devolver un objeto con el next funci贸n, y ya no tenemos que ocuparnos de devolver el {value: val, done: bool} objeto. Si bien estos ahorros pueden parecer m铆nimos en este ejemplo, su utilidad se realizar谩 f谩cilmente a medida que sus generadores crezcan en complejidad.

    Ventajas

    Como Jake Archibald se帽ala, algunas ventajas de estos generadores son:

    • pereza: Los valores no se calculan de antemano, por lo que si no itera hasta el final, no habr谩 perdido el tiempo calculando los valores no utilizados.
    • Infinito: Dado que los valores no se calculan de antemano, puede obtener un conjunto infinito de valores. Solo aseg煤rate de salir del bucle en alg煤n momento.
    • Iteraci贸n de cadenas: Gracias a Symbol.iterator, String ahora tiene su propio iterador para facilitar mucho la repetici贸n de caracteres. Iterar sobre los s铆mbolos de caracteres de una cadena puede ser un verdadero dolor de cabeza. Esto es especialmente 煤til ahora que JavaScript ES5 es compatible con Unicode.

      for (var s铆mbolo de cadena) {
      console.log (s铆mbolo);
      }

    Conclusi贸n

    Si bien los iteradores y generadores no son grandes caracter铆sticas adicionales, ayudan bastante a limpiar el c贸digo y mantenerlo organizado. Mantener la l贸gica de iteraci贸n con el objeto al que pertenece es una buena pr谩ctica, que parece ser gran parte del enfoque de las caracter铆sticas de ES6. El est谩ndar parece estar avanzando hacia la capacidad de estructura y facilidad de dise帽o de Java, al tiempo que mantiene la velocidad de desarrollo de lenguajes din谩micos.

    驴Qu茅 opinas de las nuevas funciones de ES6? 驴Qu茅 tipo de casos de uso interesantes tiene para iteradores y generadores? 隆H谩znoslo saber en los comentarios!

    Gracias a Jake Archibald por el gran art铆culo que detalla gran parte de c贸mo funcionan los iteradores y generadores en ES6.

     

    Etiquetas:

    Deja una respuesta

    Tu direcci贸n de correo electr贸nico no ser谩 publicada. Los campos obligatorios est谩n marcados con *