Iterables:带有迭代器的对象或生成器

时间:2017-07-14 01:55:05

标签: javascript ecmascript-6 generator iterable

让我们假设一个具有已定义迭代器的对象的两个类似实现:一个迭代器使用generators,另一个使用iterables。这两个都与Array.from一起使用,并且它们都可以迭代。这两种方法有哪些区别,哪一种更受欢迎,为什么?是否需要较小的方法?

class Foo {
  constructor( ...args ) {
    this.f = args;
  }
  [Symbol.iterator]() {
    let c = 0;

    const i = {

      next: () => {
        if ( c < this.f.length ) {
          return {value:this.f[c++], done: false};
        }
        else {
          return {value:undefined,done:true};
        }
      }

    };
    return i;
  }

};

class Bar {
  constructor( ...args ) {
    this.f = args;
  }
  *[Symbol.iterator]() {
    let c = 0;

    if ( c < this.f.length ) {
      yield this.f[c++];
    }
    else {
      return;
    }
  }

};

在这里,我们可以测试它们,以表明它们基本相同。

var o1 = new Foo([1,2,3]);
for ( let x of o1 ) {
  console.warn(x)
}
console.log(o1, Array.from(o1));

var o2 = new Bar([1,2,3]);
for ( let x of o2 ) {
  console.warn(x)
}
console.log(o2, Array.from(o2));

1 个答案:

答案 0 :(得分:5)

  

具有已定义迭代器的对象的两个类似实现:一个使用生成器的迭代器,另一个使用迭代器。

首先让术语更正:您已经为Iterables定义了两个(构造函数)对象。它们都是可迭代的,因为它们有Symbol.iterator方法返回迭代器 - 一个带有next方法的对象。其中一种方法是通过字面返回一个对象来实现的,另一种是使用生成器语法实现的。

  

我们可以对它们进行测试,以证明它们基本相同。

呃,不,你犯了一个重大错误:你在构造函数中使用了rest参数,所以你的两个对象都以一个数组的数组作为f

如果您使用var o = new FooBar(1, 2, 3)constructor(args) {,则该属性将符合您的预期,并且示例将表明他们绝对不会做同样的事情。

因此,让我们修复您的代码:

&#13;
&#13;
class Test {
  constructor(arr) {
    this.f = arr;
  }
}
class Foo extends Test {
  [Symbol.iterator]() {
    let c = 0;
    return {
      next: () => {
        if ( c < this.f.length ) {
          return {value: this.f[c++], done: false};
        } else {
          return {value: undefined, done: true};
        }
      }
    };
  }
}
class Bar extends Test {
  *[Symbol.iterator]() {
    let c = 0;
    while (c < this.f.length) // written a lot nicer using a `for` loop
      yield this.f[c++];
    // return undefined; // we really should omit that
  }
}

for (let test of [Foo, Bar]) {
  console.log(test.name);
  const o = new test([1,2,3]);
  for (const x of o)
    console.log(x)
  console.log(Array.from(o));
}
&#13;
&#13;
&#13;

现在这就是你真正想要的。

  

这两种方法有何不同?

我希望从上面的代码中可以清楚地看出:生成器函数更加简单。

  

哪一个更受欢迎,为什么?

猜测:-)句法糖通过抽象提高可读性并简化复杂的行为。

  

是否需要较小的方法?

我无法想象任何标准用例。当然,生成器语法是引擎需要支持的功能,但完整的迭代协议也是如此。也许存在一些边缘情况,其中手工制作的微优化迭代器对象比发生器更快/更便宜/更轻,例如,对于恒定的无限迭代器,但我对此表示怀疑。