For AI agents: the complete documentation index is available at /tc39-atlas/llms.txt, the full documentation bundle is available at /tc39-atlas/llms-full.txt, and this page is available as Markdown at /tc39-atlas/proposals/proposal-iterator-helpers.md.
  • 简体中文
  • Sync Iterator helpers S4

    中文标题:同步迭代器辅助方法

    提案概览
    提案速览

    该提案向 ECMAScript 中的 Iterator 原型添加一组辅助方法,如 map、filter、take、drop、flatMap、reduce、toArray、forEach、some、every 和 find,以及一个静态的 Iterator.from 方法。它旨在使迭代器像数组一样易于使用,并解决常见的使用模式。

    Note

    以下 README 来自上游仓库,其中的阶段或状态标注可能滞后;当前信息以提案概览为准。

    迭代器辅助方法

    IMPORTANT


    此提案现已是 Stage 4,并将在 https://github.com/tc39/ecma262/pull/3395 中合并到 ECMA-262。此仓库不再活跃。

    一个关于若干接口的提案,以帮助 ECMAScript 中迭代器的通用使用和消费。

    状态

    作者:Gus Caplan, Michael Ficarra, Adam Vandolder, Jason Orendorff, Kevin Gibbons

    提案发起人:Michael Ficarra, Yulia Startsev

    此提案处于 TC39 流程 的 Stage 4。

    此提案原先包含异步和同步辅助方法。异步辅助方法已拆分到 单独提案

    动机

    迭代器是表示大型或可能无限的可枚举数据集的有用方式。然而,它们缺少使它们像数组和其他有限数据结构一样易于使用的辅助方法,这导致某些问题更适合用迭代器表示却被表达为数组,或使用库来引入必要的辅助方法。许多 库和语言 已经提供了这些接口。

    提案

    该提案在 Iterator 原型上引入了一系列新方法,以允许对迭代器进行通用使用和消费。关于所实现方法的具体内容,请参考规范。

    关于语义决策的细节,请参见 DETAILS.md

    参见此处渲染的提案 此处

    添加的方法

    对于迭代器,我们添加以下方法:

    .map(mapperFn)

    map 接受一个函数作为参数。它允许用户对迭代器返回的每个元素应用一个函数。

    返回应用了映射函数后的值的迭代器。

    示例
    function* naturals() {
      let i = 0;
      while (true) {
        yield i;
        i += 1;
      }
    }
    
    const result = naturals()
      .map(value => {
        return value * value;
      });
    result.next(); //  {value: 0, done: false};
    result.next(); //  {value: 1, done: false};
    result.next(); //  {value: 4, done: false};

    .filter(filtererFn)

    filter 接受一个函数作为参数。它允许用户跳过迭代器中未通过过滤函数的值。

    返回原始迭代器中通过过滤器的值的迭代器。

    示例
    function* naturals() {
      let i = 0;
      while (true) {
        yield i;
        i += 1;
      }
    }
    
    const result = naturals()
      .filter(value => {
        return value % 2 == 0;
      });
    result.next(); //  {value: 0, done: false};
    result.next(); //  {value: 2, done: false};
    result.next(); //  {value: 4, done: false};

    .take(limit)

    take 接受一个整数作为参数。它返回一个迭代器,该迭代器最多产生底层迭代器产生的给定数量的元素。

    返回一个迭代器,包含原始迭代器中从 0 到限制数量的元素。

    示例
    function* naturals() {
      let i = 0;
      while (true) {
        yield i;
        i += 1;
      }
    }
    
    const result = naturals()
      .take(3);
    result.next(); //  {value: 0, done: false};
    result.next(); //  {value: 1, done: false};
    result.next(); //  {value: 2, done: false};
    result.next(); //  {value: undefined, done: true};

    .drop(limit)

    drop 接受一个整数作为参数。它跳过底层迭代器产生的给定数量的元素,然后自身产生剩余元素。

    返回限制数量之后的元素的迭代器。

    示例
    function* naturals() {
      let i = 0;
      while (true) {
        yield i;
        i += 1;
      }
    }
    
    const result = naturals()
      .drop(3);
    result.next(); //  {value: 3, done: false};
    result.next(); //  {value: 4, done: false};
    result.next(); //  {value: 5, done: false};

    .flatMap(mapperFn)

    .flatMap 接受一个映射函数作为参数。它返回一个迭代器,该迭代器产生所有由将映射函数应用于底层迭代器产生的元素而得到的迭代器中的所有元素。

    返回扁平值的迭代器。

    示例
    const sunny = ["It's Sunny in", "", "California"].values();
    
    const result = sunny
      .flatMap(value => value.split(" ").values());
    result.next(); //  {value: "It's", done: false};
    result.next(); //  {value: "Sunny", done: false};
    result.next(); //  {value: "in", done: false};
    result.next(); //  {value: "", done: false};
    result.next(); //  {value: "California", done: false};
    result.next(); //  {value: undefined, done: true};

    .reduce(reducer [, initialValue ])

    reduce 接受一个函数和一个可选的初始值作为参数。它允许用户对迭代器返回的每个元素应用一个函数,同时跟踪最近一次 reducer 的结果(记忆值)。对于第一个元素,使用给定的初始值作为记忆值。

    返回一个值(在示例中为数字),其类型是返回给 reducer 函数的类型。

    示例
    function* naturals() {
      let i = 0;
      while (true) {
        yield i;
        i += 1;
      }
    }
    
    const result = naturals()
      .take(5)
      .reduce((sum, value) => {
        return sum + value;
      }, 3);
    
    result // 13

    .toArray()

    当你有一个非无限的迭代器并希望将其转换为数组时,你可以使用内置的 toArray 方法来实现。

    返回一个包含迭代器值的数组。

    示例
    function* naturals() {
      let i = 0;
      while (true) {
        yield i;
        i += 1;
      }
    }
    
    const result = naturals()
      .take(5)
      .toArray();
    
    result // [0, 1, 2, 3, 4]

    .forEach(fn)

    为了对迭代器使用副作用,你可以使用内置的 .forEach 方法,它接受一个函数作为参数。

    返回 undefined。

    示例
    const log = [];
    const fn = (value) => log.push(value);
    const iter = [1, 2, 3].values();
    
    iter.forEach(fn);
    console.log(log.join(", ")) // "1, 2, 3"

    .some(fn)

    要检查迭代器中是否有任何值匹配给定的谓词,可以使用 .some。它接受一个返回 true 或 false 的函数作为参数。

    返回一个布尔值,如果对任何元素调用 fn 返回 true,则为 true。调用 some 时,迭代器将被消费。

    示例
    function* naturals() {
      let i = 0;
      while (true) {
        yield i;
        i += 1;
      }
    }
    
    const iter = naturals().take(4);
    
    iter.some(v => v > 1); // true
    iter.some(v => true); // false, iterator is already consumed.
    
    naturals().take(4).some(v => v > 1); // true
    naturals().take(4).some(v => v == 1); // true, acting on a new iterator

    .every(fn)

    .every 接受一个返回布尔值的函数作为参数。它用于检查迭代器生成的每个值是否都通过测试函数。

    返回一个布尔值。

    function* naturals() {
      let i = 0;
      while (true) {
        yield i;
        i += 1;
      }
    }
    
    const iter = naturals().take(10);
    
    iter.every(v => v >= 0); // true
    iter.every(v => false); // true, iterator is already consumed.
    
    naturals().take(4).every(v => v > 0); // false, first value is 0
    naturals().take(4).every(v => v >= 0); // true, acting on a new iterator

    .find(fn)

    .find 接受一个函数作为参数。它用于在迭代器中查找第一个匹配的元素。

    可以在无限迭代器上使用而不需要 take

    返回找到的元素,如果没有元素匹配 fn,则返回 undefined

    function* naturals() {
      let i = 0;
      while (true) {
        yield i;
        i += 1;
      }
    }
    
    naturals().find(v => v > 1); // 2

    Iterator.from(object)

    .from 是一个 静态 方法(与上述其他方法不同),它接受一个对象作为参数。此方法允许用迭代器包装“类似迭代器”的对象。

    如果对象已经是迭代器,则返回该对象;如果传递的对象实现了可调用的 @@iterator 属性,则返回一个包装迭代器。

    class Iter {
      next() {
        return { done: false, value: 1 };
      }
    }
    
    const iter = new Iter();
    const wrapper = Iterator.from(iter);
    
    wrapper.next() // { value: 1, done: false }

    迭代器辅助方法与生成器协议

    生成器协议有助于协调生产者和消费者,而这种协调必然会被基于迭代的转换所破坏。无法正确保留或重建这种协调。我们采取的理念是,本提案添加的辅助方法产生的任何迭代器仅实现迭代器协议,而不尝试支持使用生成器协议其余部分的生成器。具体来说,此类迭代器不实现 .throw,也不将 .next.return 的参数转发给底层或“源”迭代器。

    扩展迭代器原型

    有了这个提案,为自定义类扩展 IteratorPrototype 将变得更加容易。请参见下面的示例,比较之前的实现和新实现。

    const MyIteratorPrototype = {
      next() {},
      throw() {},
      return() {},
    
      // but we don't properly implement %IteratorPrototype%!!!
    };
    
    // Previously...
    // Object.setPrototypeOf(MyIteratorPrototype,
    //   Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]())));
    
    Object.setPrototypeOf(MyIteratorPrototype, Iterator.prototype);

    问答

    为什么不使用 Array.from + Array.prototype 方法?

    本提案中所有产生迭代器的方法都是惰性的。它们只会在需要下一个项目时才消费迭代器。对于永不结束的迭代器,这一点至关重要。没有通用支持任何形式的迭代器,不同的迭代器必须以不同的方式处理。

    如何访问新的内部方法?

    const IteratorHelperPrototype = Object.getPrototypeOf(Iterator.from([]).take(0));
    const WrapForValidIteratorPrototype = Object.getPrototypeOf(Iterator.from({ next(){} }));

    先前艺术与用户态实现

    方法RustPythonnpm ItertoolsC#
    all
    any
    chain
    collect
    count
    cycle
    enumerate
    filter
    filterMap
    find
    findMap
    flatMap
    flatten
    forEach
    last
    map
    max
    min
    nth
    partition
    peekable
    position
    product
    reverse
    scan
    skip
    skipWhile
    stepBy
    sum
    take
    takeWhile
    unzip
    zip
    compress
    permutations
    repeat
    slice
    starmap
    tee
    compact
    contains
    range
    reduce
    sorted
    unique
    average
    empty
    except
    intersect
    prepend
    append

    注意:方法名称已组合,例如 toArraycollect