【问题标题】:How to make an iterator out of an ES6 class如何从 ES6 类中创建一个迭代器
【发布时间】:2015-04-28 16:05:07
【问题描述】:

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

[编辑 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]() 并在 asterix 之后直接显示“预期函数名称”警告,但除此之外,它可以编译并使用 Traceur 正常运行。 (注意 WebStorm 不会为 *generator() 生成任何错误。)

【问题讨论】:

  • 这段代码在节点v8.1.1中对我来说很好

标签: javascript ecmascript-6 ecmascript-harmony


【解决方案1】:

您需要为SomeClass 指定Symbol.iterator 属性,该属性为类实例返回iterator。迭代器必须有next() 方法,女巫反过来返回带有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) }
    }
  };
};

或者使用 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) }

在您更新的问题中,您通过生成器函数实现了类迭代器。您可以这样做,但您必须了解迭代器不能成为生成器。实际上 es6 中的迭代器是具有特定 next() method 的任何对象

【讨论】:

  • 这确实有效,比标记为正确的答案更好。谢谢!
  • 为了使 ES5 代码正常工作,我必须像在 ES6 代码中一样设置index = -1。可能的错误?
  • @alexpods 我只是想问一下,我注意到了你的 done 声明。在控制台中测试它时,它永远不会有真相。我正在测试它。如果它是 python 它实际上会工作,但我不确定这是否是一个错误?
  • 想知道,有了这个自定义迭代器,有没有一种简单的方法可以将生成的数据直接转换为数组?考虑添加一个类似“toArray”的方法,在内部调用迭代器并填充数组,但也许有更好的方法?
【解决方案2】:

定义一个合适的迭代器方法。例如:

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)

【讨论】:

  • 并以星号为前缀,以用作生成器函数。
  • 这个特定的例子没有使用生成器。它只是委托给一个数组迭代器。
  • 您能否添加一个正在使用的代码示例?我无法让它工作。
  • @timkay 你可能无法让它工作,因为在大多数实现中数组still don't have a .values() method。请改用this.a[Symbol.iterator]()
  • 谢谢!正如@bergi 指出的那样,大多数实现不支持数组的 values() 。他的解决方案更普遍。也许你可以更新你的代码。
【解决方案3】:

这是一个在 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

【讨论】:

  • 注意:以上代码可能需要最新版本的nodeJS才能工作。使用node v8.1.1 编译得很好
  • 您可以使用for...of 循环简化迭代器:for (let row of this.matrix) { for (let cell of row) { yield cell; } }
  • @LukeMWillis - 不错!好多了。更新答案
【解决方案4】:

文档:Iteration Protocols

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

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

【讨论】:

  • 未来读者须知; while 的最后一个循环如果没有继续进行 let element;... 就无法自行运行,这对于某些 JavaScript shell 来说很好。否则是可靠的答案。
【解决方案5】:

说明

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

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

实施

我还将为一个名为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};
    }
}

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

【讨论】:

  • 参考 - Eloquent Javascript
【解决方案6】:

存储在子对象中的 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;
    }
}

【讨论】:

    【解决方案7】:

    我认为没有人发布过异步迭代器的示例,所以请看:

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

    用法:

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

    【讨论】:

      猜你喜欢
      • 2016-07-24
      • 2018-05-20
      • 2021-12-06
      • 2016-09-23
      • 2021-10-15
      • 1970-01-01
      • 2010-10-09
      • 1970-01-01
      • 2011-01-03
      相关资源
      最近更新 更多