it-roy-ru.com

Как сделать итератор из класса ES6

Как бы я сделал итератор из класса ES6 таким же образом, как синтаксис JS1.7 SomeClass.prototype.__iterator__ = function() {...}?

[Правка 16:00]

Следующие работы:

class SomeClass {
    constructor() {
    }

    *[Symbol.iterator]() {
        yield '1';
        yield '2';
    }

    //*generator() {
    //}

}

an_instance = new SomeClass();
for (let v of an_instance) {
    console.log(v);
}

WebStorm помечает *[Symbol.iterator]() предупреждением «имя функции ожидается» сразу после звездочки, но в остальном это компилируется и работает нормально с Traceur. (Примечание. WebStorm не генерирует ошибок для *generator().)

50
user5321531

Определите подходящий метод итератора. Например:

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

Правка: Пример использования:

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

Вам необходимо указать свойство Symbol.iterator для SomeClass, которое возвращает итератор для экземпляров класса. Итератор должен иметь метод next(), который в свою очередь возвращает объект с полями done и value. Упрощенный пример:

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) }
    }
  };
};

Или используя классы ES6 и функции стрелок:

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) })
    };
  };
}

И использование:

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

В своем обновленном вопросе вы реализовали класс iterator через generator function. Вы можете сделать это, но вы должны понимать, что итератор НЕ МОЖЕТ БЫТЬ генератором. На самом деле итератор в es6 - это любой объект, который имеет определенный метод next()

27
alexpods

Вот пример итерации для пользовательского класса 2d матрицы в 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;
            }
        }
    }
}

Использование такого класса будет

let matrix = new Matrix();

for (let cell of matrix) {
    console.log(cell)
}

Который будет выводить

1
2
9
5
3
8
4
6
7
18
Shaheen Ghiassy

Документация: Протоколы итераций

Пример класса, реализующий методы iterator protocol и 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();
  }
}

Доступ к элементам с использованием любой техники:

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);
9
ekillaby

Пример класса итератора ES6, который хранится в подобъекте:

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;
    }
}
0
kyleo347

Объяснение

Создание объекта iterable означает, что у этого объекта есть метод с именем Symbol.iterator. Когда этот метод вызывается, он должен возвращать интерфейс с именем iterator.

Этот итератор должен иметь метод next, который возвращает следующий результат. Этот результат должен быть объектом со свойством value, которое предоставляет следующее значение, и свойством done, которое должно быть true, когда больше нет результатов, и false в противном случае.

Реализация

Я также буду реализовывать итератор для класса с именем Matrix, все элементы которого будут варьироваться от 0 до width * height - 1. Я создам другой класс для этого итератора с именем 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};
    }
}

Обратите внимание, что Matrix реализует протокол iterator, определяя символ Symbol.iterator. Внутри этого метода создается экземпляр MatrixIterator, который принимает this, т.е. экземпляр Matrix в качестве параметра, а внутри MatrixIterator определяется метод next. Мне особенно нравится такой способ реализации итератора, потому что он ясно показывает iterator и реализацию Symbol.iterator.

Кроме того, можно также не определять непосредственно Symbol.iterator, а вместо этого добавить функцию к prototype[Symbol.iterator] следующим образом:

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

Пример использования

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