简体   繁体   English

如何用 ES6 类制作迭代器

[英]How to make an iterator out of an ES6 class

How would I make an iterator out of an ES6 class in the same manner as JS1.7 SomeClass.prototype.__iterator__ = function() {...} syntax?我如何以与 JS1.7 SomeClass.prototype.__iterator__ = function() {...}语法相同的方式从 ES6 类中创建SomeClass.prototype.__iterator__ = function() {...}

[EDIT 16:00] [编辑 16:00]

The following works:以下工作:

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]() with a 'function name expected' warning directly following the asterix, but otherwise this compiles and runs fine with Traceur. WebStorm 标记*[Symbol.iterator]()并在星号后直接带有“函数名称预期”警告,但除此之外,它可以编译并与 Traceur 一起正常运行。 (Note WebStorm does not generate any errors for *generator() .) (注意 WebStorm 不会为*generator()生成任何错误。)

You need to specify Symbol.iterator property for SomeClass which returns iterator for class instances.您需要为SomeClass指定Symbol.iterator属性,它返回类实例的迭代器 Iterator must have next() method, witch in turn returns object with done and value fields.迭代器必须有next()方法,女巫依次返回带有donevalue字段的对象。 Simplified example:简化示例:

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

Or using ES6 classes and arrow functions:或者使用 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) })
    };
  };
}

And usage:和用法:

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

In your updated question you realized class iterator through generator function .在您更新的问题中,您通过生成器函数实现了类迭代 You can do so, but you must understand that iterator COULD NOT BE a generator.你可以这样做,但你必须明白迭代器不能是一个生成器。 Actually iterator in es6 is any object that has specific next() method实际上 es6 中的迭代器是具有特定next()方法的任何对象

Define a suitable iterator method.定义合适的迭代器方法。 For example:例如:

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

Edit: Sample use:编辑:示例使用:

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

Here's an example for iterating over a 2d matrix custom class in ES6这是在 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;
            }
        }
    }
}

The usage of such a class would be这样一个类的用法是

let matrix = new Matrix();

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

Which would output哪个会输出

1
2
9
5
3
8
4
6
7

Documentation: Iteration Protocols文档: 迭代协议

Example class implementing both iterator protocol and iterable protocol techniques:实现迭代器协议可迭代协议技术的示例类:

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

Access elements using either 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);

Explanation解释

Making an object iterable means this object has a method named with the Symbol.iterator .使对象可迭代意味着该对象有一个以Symbol.iterator命名的方法。 When this method gets called, it should return an interface called iterator .当这个方法被调用时,它应该返回一个名为iterator的接口。

This iterator must have a method next that returns the next result.这个迭代器必须有一个方法next返回下一个结果。 This result should be an object with a value property that provides the next value, and a done property, which should be true when there are no more results and false otherwise.这个结果应该是一个对象,它有一个提供下一个值的value属性和一个done属性,当没有更多结果时应该为true ,否则为false

Implementation执行

I will also implement an iterator for a class called Matrix which all elements will range from 0 to width * height - 1 .我还将为一个名为Matrix的类实现一个迭代器,其中所有元素的范围都从0width * height - 1 I will create a different class for this iterator called MatrixIterator .我将为这个迭代器创建一个不同的类,称为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};
    }
}

Notice that Matrix implements the iterator protocol by defining Symbol.iterator symbol.请注意, Matrix通过定义Symbol.iterator符号来实现迭代器协议。 Inside this method, an instance of MatrixIterator is created which takes this , ie, the Matrix instance as parameter, and inside MatrixIterator , the method next is defined.在这个方法中,创建了一个MatrixIterator的实例,它以this为参数,即Matrix实例作为参数,在MatrixIterator ,定义了next方法。 I particularly like this way of implementing an iterator because it clearly shows the iterator and the implementation of the Symbol.iterator .我特别喜欢这种实现迭代器的方式,因为它清楚地展示了迭代器Symbol.iterator的实现。

Alternatively, one can also not define directly Symbol.iterator , and instead add a function to prototype[Symbol.iterator] as follows:或者,也可以不直接定义Symbol.iterator ,而是在prototype[Symbol.iterator]添加一个函数,如下所示:

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

Usage Example使用示例

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

Example of an ES6 iterator class that stores in a sub-object:存储在子对象中的 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;
    }
}

I don't think anyone's posted an example of an async iterator, so here you go:我认为没有人发布过异步迭代器的例子,所以你去吧:

class TableReadStream<T extends any[]> {
    async *[Symbol.asyncIterator]() {
        let row: T
        while((row = await this.readRow()) !== null) {
            yield row
        }
    }
}

Usage:用法:

for await(let row of readStream) {
    console.log(row)
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM