javascript 如何用 ES6 类制作迭代器

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28739745/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-28 09:28:46  来源:igfitidea点击:

How to make an iterator out of an ES6 class

javascriptecmascript-6ecmascript-harmony

提问by user5321531

How would I make an iterator out of an ES6 class in the same manner as JS1.7 SomeClass.prototype.__iterator__ = function() {...}syntax?

我将如何以与 JS1.7SomeClass.prototype.__iterator__ = function() {...}语法相同的方式从 ES6 类中创建迭代器?

[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. (Note WebStorm does not generate any errors for *generator().)

WebStorm*[Symbol.iterator]()会在星号后直接标出带有“预期函数名称”警告的标志,但除此之外,它可以通过 Traceur 编译并运行良好。(注意 WebStorm 不会为 生成任何错误*generator()。)

采纳答案by Andreas Rossberg

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)

回答by alexpods

You need to specify Symbol.iteratorproperty for SomeClasswhich returns iteratorfor class instances. Iterator must have next()method, witch in turn returns object with doneand valuefields. Simplified example:

您需要指定为类实例返回迭代器的Symbol.iterator属性。迭代器必须有方法,女巫依次返回带有和字段的对象。简化示例:SomeClassnext()donevalue

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 iteratorthrough 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()

回答by Shaheen Ghiassy

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

回答by ekillaby

Documentation: Iteration Protocols

文档:迭代协议

Example class implementing both iterator protocoland iterable protocoltechniques:

实现迭代器协议可迭代协议技术的示例类:

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

回答by lmiguelvargasf

Explanation

解释

Making an object iterablemeans this object has a method named with the Symbol.iterator. When this method gets called, it should return an interface called iterator.

使对象可迭代意味着该对象有一个以Symbol.iterator. 当这个方法被调用时,它应该返回一个名为iterator的接口。

This iteratormust have a method nextthat returns the next result. This result should be an object with a valueproperty that provides the next value, and a doneproperty, which should be truewhen there are no more results and falseotherwise.

这个迭代器必须有一个next返回下一个结果的方法。这个结果应该是一个具有value提供下一个值的属性的对象,以及一个done属性,true当没有更多结果时应该是这样,false否则。

Implementation

执行

I will also implement an iterator for a class called Matrixwhich all elements will range from 0to width * height - 1. I will create a different class for this iterator called MatrixIterator.

我还将为一个名为的类实现一个迭代器Matrix,所有元素的范围都从0width * 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};
    }
}

Notice that Matriximplements the iteratorprotocol by defining Symbol.iteratorsymbol. Inside this method, an instance of MatrixIteratoris created which takes this, i.e., the Matrixinstance as parameter, and inside MatrixIterator, the method nextis defined. I particularly like this way of implementing an iterator because it clearly shows the iteratorand the implementation of the Symbol.iterator.

注意通过定义符号来Matrix实现迭代器协议Symbol.iterator。在这个方法内部,MatrixIterator创建了一个实例,它以为参数,thisMatrix实例作为参数,在内部 MatrixIteratornext定义了方法。我特别喜欢这种实现迭代器的方式,因为它清楚地展示了迭代器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);
}

回答by kyleo347

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