【问题标题】:Iterables: Objects with iterators, or generators可迭代对象:具有迭代器或生成器的对象
【发布时间】:2017-12-18 23:46:08
【问题描述】:

让我们假设具有定义迭代器的对象的两个类似实现:一个使用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));

【问题讨论】:

  • 对我来说,生成器只是创建迭代器的语法糖。当然,它是一种非常强大的语法糖,代码越复杂,因为您不必自己维护迭代器状态。就我个人而言,由于样板较少,我总是更喜欢生成器。

标签: javascript ecmascript-6 generator iterable


【解决方案1】:

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

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

我们可以测试它们以证明它们本质上是相同的。

呃,不,你犯了一个根本性的错误:你在构造函数中使用了 rest 参数,所以你的两个对象都以一个数组的数组作为它们的 f 值。

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

所以让我们修复你的代码:

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

这现在可以满足您的实际需求。

这两种方法有什么区别?

我希望从上面的代码中可以清楚地看到:生成器函数要简单得多。

首选哪一个,为什么?

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

是否需要采用较小的方法?

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

【讨论】:

  • 我已经澄清了标题。您能否进一步回答是否有更好地使用带有迭代器的对象,而不是生成器?
  • 我无法想象任何现实世界的用例。当然,当它们的语法不受支持时,生成器就会被淘汰,但在这种情况下,所有其他 ES6 特性也不会。也许在某些极端情况下,手工制作的迭代器对象比生成器更快/更便宜/更轻/更好,例如用于恒定的无限迭代器。
  • 如果您用它更新答案,我会将其标记为已选择。这涵盖了我想学习的所有内容。
猜你喜欢
  • 2023-03-20
  • 2016-04-16
  • 1970-01-01
  • 2018-08-16
  • 1970-01-01
  • 1970-01-01
  • 2019-06-03
  • 2011-11-22
  • 1970-01-01
相关资源
最近更新 更多