Comment créer un itérateur à partir d'une classe ES6 de la même manière que la syntaxe JS1.7 SomeClass.prototype.__iterator__ = function() {...}
?
[EDIT 16:00]
Les oeuvres suivantes:
class SomeClass {
constructor() {
}
*[Symbol.iterator]() {
yield '1';
yield '2';
}
//*generator() {
//}
}
an_instance = new SomeClass();
for (let v of an_instance) {
console.log(v);
}
WebStorm marque *[Symbol.iterator]()
avec l'avertissement 'nom de fonction attendu' directement après l'astérisque, mais sinon, cela se compile et fonctionne correctement avec Traceur. (Remarque: WebStorm ne génère aucune erreur pour *generator()
.)
Définissez une méthode d'itérateur appropriée. Par exemple:
class C {
constructor() { this.a = [] }
add(x) { this.a.Push(x) }
[Symbol.iterator]() { return this.a.values() }
}
Edit: Exemple d'utilisation:
let c = new C
c.add(1); c.add(2)
for (let i of c) console.log(i)
Vous devez spécifier la propriété Symbol.iterator
pour SomeClass
qui renvoie iterator pour les instances de classe. Iterator doit avoir la méthode next()
, qui à son tour renvoie un objet avec les champs done
et value
. Exemple simplifié:
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) }
}
};
};
Ou en utilisant les classes ES6 et les fonctions de flèche:
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) })
};
};
}
Et utilisation:
var obj = new SomeClass();
for (var i of obj) { console.log(i) }
Dans votre question mise à jour, vous avez réalisé les fonctions class iterator à generator. Vous pouvez le faire, mais vous devez comprendre que l’itérateur ne peut pas être un générateur. En fait, iterator dans es6 est un objet qui a une méthode spécifique next()
Voici un exemple d'itération sur une classe personnalisée de matrice 2d dans 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;
}
}
}
}
L’utilisation d’une telle classe serait
let matrix = new Matrix();
for (let cell of matrix) {
console.log(cell)
}
Quel serait sortie
1
2
9
5
3
8
4
6
7
Documentation: Protocoles d'itération
Exemple de classe implémentant les techniques iterator protocol et iterable protocol:
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();
}
}
Accéder aux éléments en utilisant l'une ou l'autre technique:
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);
Exemple de classe d'itérateur ES6 stockée dans un sous-objet:
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;
}
}
Faire un objet iterable signifie que cet objet a une méthode nommée avec le Symbol.iterator
. Lorsque cette méthode est appelée, elle devrait renvoyer une interface appelée iterator .
Ce itérateur doit avoir une méthode next
qui retourne le résultat suivant. Ce résultat doit être un objet avec une propriété value
qui fournit la valeur suivante et une propriété done
, qui devrait être true
lorsqu'il n'y a plus de résultats et false
sinon.
Je vais également implémenter un itérateur pour une classe appelée Matrix
dont tous les éléments seront compris entre 0
et width * height - 1
. Je vais créer une classe différente pour cet itérateur appelé MatrixIterator
.
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};
}
}
Notez que Matrix
implémente le protocole iterator en définissant le symbole Symbol.iterator
. Dans cette méthode, une instance de MatrixIterator
est créée. Elle prend this
, c'est-à-dire l'instance Matrix
en tant que paramètre, et dans MatrixIterator
, la méthode next
est définie. J'aime particulièrement cette façon de mettre en œuvre un itérateur car elle montre clairement le itérateur et la mise en œuvre du Symbol.iterator
.
Sinon, on ne peut pas non plus définir directement Symbol.iterator
, mais ajouter une fonction à prototype[Symbol.iterator]
comme suit:
Matrix.prototype[Symbol.iterator] = function() {
return new MatrixIterator(this);
};
let matrix = new Matrix(3, 2);
for (let e of matrix) {
console.log(e);
}