what que mdn generadores for es6 javascript ecmascript-6 ecmascript-harmony

javascript - que - Cómo hacer un iterador de una clase ES6



what is function*in javascript (6)

Explicación

Hacer que un objeto sea iterable significa que este objeto tiene un método nombrado con el Symbol.iterator . Cuando se llama a este método, debe devolver una interfaz llamada iterador .

Este iterador debe tener un método next que devuelva el siguiente resultado. Este resultado debe ser un objeto con una propiedad de value que proporcione el siguiente valor y una propiedad de done , que debe ser true cuando no haya más resultados y false caso contrario.

Implementación

También implementaré un iterador para una clase llamada Matrix que todos los elementos oscilarán entre 0 y width * height - 1 . MatrixIterator una clase diferente para este iterador llamada MatrixIterator .

class Iterator { data; constructor(data = {}) { this.data = JSON.parse(JSON.stringify(data)); } add(key, value) { this.data[key] = value; } get(key) { return this.data[key]; } [Symbol.iterator]() { const keys = Object.keys(this.data).filter(key => this.data.hasOwnProperty(key)); const values = keys.map(key => this.data[key]).values(); return values; } }

Observe que Matrix implementa el protocolo iterador definiendo el símbolo Symbol.iterator . Dentro de este método, se crea una instancia de MatrixIterator que toma this , es decir, la instancia de Matrix como parámetro, y dentro de MatrixIterator , se define el next método. Particularmente me gusta esta forma de implementar un iterador porque muestra claramente el iterador y la implementación de Symbol.iterator .

Alternativamente, tampoco se puede definir directamente Symbol.iterator , y en su lugar agregar una función al prototype[Symbol.iterator] siguiente manera:

class Matrix { constructor(width, height) { this.width = width; this.height = height; this.content = []; for (let y = 0; y < height; y++) { for (let x = 0; x < width; x++) { this.content[y * width + x] = y * width + x; } } } get(x, y) { return this.content[y * this.width + x]; } [Symbol.iterator]() { return new MatrixIterator(this); } } class MatrixIterator { constructor(matrix) { this.x = 0; this.y = 0; this.matrix = matrix; } next() { if (this.y == this.matrix.height) return {done: true}; let value = { x: this.x, y: this.y, value: this.matrix.get(this.x, this.y) }; this.x++; if (this.x == this.matrix.width) { this.x = 0; this.y++; } return {value, done: false}; } }

Ejemplo de uso

Matrix.prototype[Symbol.iterator] = function() { return new MatrixIterator(this); };

¿Cómo haría un iterador de una clase ES6 de la misma manera que la sintaxis SomeClass.prototype.__iterator__ = function() {...} ?

[EDITAR 16:00]

Los siguientes trabajos:

class SomeClass { constructor() { } *[Symbol.iterator]() { yield ''1''; yield ''2''; } //*generator() { //} } an_instance = new SomeClass(); for (let v of an_instance) { console.log(v); }

WebStorm flags *[Symbol.iterator]() con una advertencia de ''nombre de función esperado'' directamente después del asterisco, pero de lo contrario, esto se compila y funciona bien con Traceur. (Nota: WebStorm no genera ningún error para *generator() .


Aquí hay un ejemplo para iterar sobre una clase personalizada de matriz 2D en ES6

class Matrix { constructor() { this.matrix = [[1, 2, 9], [5, 3, 8], [4, 6, 7]]; } *[Symbol.iterator]() { for (let row of this.matrix) { for (let cell of row) { yield cell; } } } }

El uso de tal clase sería

let matrix = new Matrix(); for (let cell of matrix) { console.log(cell) }

Lo que daría salida

1 2 9 5 3 8 4 6 7


Defina un método iterador adecuado. Por ejemplo:

class C { constructor() { this.a = [] } add(x) { this.a.push(x) } [Symbol.iterator]() { return this.a.values() } }

Editar: Uso de muestra:

let c = new C c.add(1); c.add(2) for (let i of c) console.log(i)


Documentación: iterator

Clase de ejemplo que implementa tanto el protocolo iterador como las técnicas de protocolo iterable :

class MyCollection { constructor(elements) { if (!Array.isArray(elements)) throw new Error(''Parameter to constructor must be array''); this.elements = elements; } // Implement "iterator protocol" *iterator() { for (let key in this.elements) { var value = this.elements[key]; yield value; } } // Implement "iterable protocol" [Symbol.iterator]() { return this.iterator(); } }

Acceda a los elementos utilizando cualquiera de las técnicas:

var myCollection = new MyCollection([''foo'', ''bar'', ''bah'', ''bat'']); // Access elements of the collection using iterable for (let element of myCollection) console.log(''element via "iterable": '' + element); // Access elements of the collection using iterator var iterator = myCollection.iterator(); while (element = iterator.next().value) console.log(''element via "iterator": '' + element);


Ejemplo de una clase de iterador ES6 que se almacena en un subobjeto:

let matrix = new Matrix(3, 2); for (let e of matrix) { console.log(e); }


Symbol.iterator especificar la propiedad Symbol.iterator para SomeClass que devuelve el iterator para las instancias de clase. El iterador debe tener el método next() , que a su vez devuelve el objeto con los campos done y value . Ejemplo simplificado:

function SomeClass() { this._data = [1,2,3,4]; } SomeClass.prototype[Symbol.iterator] = function() { var index = 0; var data = this._data; return { next: function() { return { value: data[++index], done: !(index in data) } } }; };

O usando clases ES6 y funciones de flecha:

class SomeClass { constructor() { this._data = [1,2,3,4]; } [Symbol.iterator]() { var index = -1; var data = this._data; return { next: () => ({ value: data[++index], done: !(index in data) }) }; }; }

Y uso:

var obj = new SomeClass(); for (var i of obj) { console.log(i) }

En su pregunta actualizada, se dio cuenta del iterador de clase a través de la función de generador . Puede hacerlo, pero debe comprender que el iterador NO PODRÍA SER un generador. En realidad, el iterador en es6 es cualquier objeto que tiene un método next() específico