Arrayの.filter().map()を連結するたびに、JavaScriptは2つの新しい配列を作成します。小さなリストでは目に見えません。大規模なデータセットや、必要に応じて値を生成するgenerator、あるいは先頭の数件の結果しか必要ないストリームの場合、すぐに破棄する何千もの要素が実体化されます。

Iterator helpersは遅延評価の代替手段です。Chrome 122、Firefox 131、Safari 18.2でネイティブに搭載されました — ライブラリもpolyfillも、ビルドステップも不要です。

「遅延評価」とは実践上どういう意味か

Arrayメソッドは即時評価です: すぐに実行され、新しい完全に埋められた配列を返します。

const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const result = numbers
  .filter(n => n % 2 === 0)  // → allocates [2, 4, 6, 8, 10]
  .map(n => n * n)            // → allocates [4, 16, 36, 64, 100]
  .slice(0, 3);               // → allocates [4, 16, 36]

Enter fullscreen mode Exit fullscreen mode

3つの配列が作成され、破棄されます。最後のものだけが残ります。

Iterator helpersは遅延評価です: パイプラインの記述を構築します。要素は要求されるまで処理されません。

const result = Iterator.from([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
  .filter(n => n % 2 === 0)
  .map(n => n * n)
  .take(3)
  .toArray();

// → [4, 16, 36]

Enter fullscreen mode Exit fullscreen mode

同じ出力です。中間配列はありません。パイプラインは要素を1つずつ処理します: 1.filter()にヒットし、失敗して破棄されます。2.filter()を通過し、.map()を通過し、.take(3)のカウントに寄与します。ループは3つの合格要素の後に停止します — 残りの数値は決して訪問されません。

Iterator.from()とその戻り値

Iterator.from()は任意のiterable(配列、Set、Map、文字列、generatorの戻り値)をIterator helperメソッドが付いたオブジェクトでラップします。

const set = new Set([1, 1, 2, 2, 3, 3, 4, 5]);
const result = Iterator.from(set)
  .filter(n => n % 2 !== 0)
  .toArray();

// → [1, 3, 5]

Enter fullscreen mode Exit fullscreen mode

Generatorは自然に適合します。Generatorはすでに必要に応じて値を生成します; Iterator helpersを使用すると、中間配列を作成せずにそれらに対してパイプラインを構築できます。

function* naturals() {
  let n = 0;
  while (true) yield n++;
}

const firstFiveSquaredEvens = Iterator.from(naturals())
  .filter(n => n % 2 === 0)
  .map(n => n * n)
  .take(5)
  .toArray();

// → [0, 4, 16, 36, 64]

Enter fullscreen mode Exit fullscreen mode

無限のgeneratorに対してはArrayメソッドではこれを行うことができません — Arrayに対する.filter()は、何かを返す前に終端に到達する必要があります。

メソッドの完全なセット

Iterator helpersには10のメソッドが搭載されています。最初のグループはパイプラインを構築します(遅延評価); 2番目のグループはそれを実行します(終端):

Lazy:     .map(fn)  .filter(fn)  .take(n)  .drop(n)  .flatMap(fn)
Terminal: .toArray()  .reduce(fn, init)  .find(fn)  .some(fn)  .every(fn)  .forEach(fn)

Enter fullscreen mode Exit fullscreen mode

遅延評価メソッドを呼び出すと新しいiteratorが返されます — まだ何も実行されていません。終端メソッドを呼び出すと、必要なものが得られるまでチェーン全体を通じて値を引き出し、その後停止します。

// .find() stops at the first match; the rest of the data is never touched
const firstLongWord = Iterator.from(wordList)
  .filter(w => w.startsWith('pre'))
  .find(w => w.length > 10);

Enter fullscreen mode Exit fullscreen mode

実用例: 大規模リストの処理

10,000件のレコードのフィルタリング可能なリストをレンダリングしているとします。ユーザーは検索語を入力し、カテゴリを選択しました。ビューには最初の50件の一致が表示されます。

Arrayを使用する場合:

const visible = allRecords
  .filter(r => r.category === selectedCategory)  // walks all 10,000
  .filter(r => r.name.includes(query))           // walks the filtered set
  .slice(0, 50);                                 // finally limits

Enter fullscreen mode Exit fullscreen mode

Iterator helpersを使用する場合:

const visible = Iterator.from(allRecords)
  .filter(r => r.category === selectedCategory)
  .filter(r => r.name.includes(query))
  .take(50)
  .toArray();

Enter fullscreen mode Exit fullscreen mode

Iteratorバージョンは50件の一致が得られ次第停止します。最初の200件のレコードでそれらの50件が見つかった場合、残りの9,800件は決して検査されません。Arrayバージョンでは、最初のフィルタパスに関係なく、すべてのレコードが常に訪問されます。

Arrayメソッドを使い続けるべき場合

Iterator helpersは万能の置き換えではありません:

  • ランダムアクセスが必要な場合: Arrayは位置によるインデックスアクセスを可能にします。Iteratorは一度だけ前方に移動します。
  • データを複数回通過する必要がある場合: Iteratorは一度しか消費できません。Arrayは再利用可能です。
  • .sort()が必要な場合: ソートにはメモリ内の完全なデータセットが必要です。遅延評価のソートはありません。
  • リストが小さい場合: 10個のアイテムの場合、Iterator.from()のオーバーヘッドはノイズです。より明確に読める方を使用してください。

最も明確な信号: 大規模または潜在的に無限のソースに対してArrayメソッドを連結し、中間結果を破棄している場合、それがIterator helpersのユースケースです。

TypeScriptサポート

TypeScript 5.6でIteratorグローバル型とメソッドシグネチャが追加されました。tsconfig.jsonがES2024以降をターゲットにしている場合、自動的に利用可能です:

function* ids(): Generator<number> {
  let id = 0;
  while (true) yield id++;
}

const first10: number[] = Iterator.from(ids()).take(10).toArray();

Enter fullscreen mode Exit fullscreen mode

以前のlibターゲットの場合、lib配列に明示的に"ES2024"を追加するか、polyfillingしている場合は@types/core-jsを使用する必要があるかもしれません。

ブラウザサポート

Iterator helpersはBaseline 2024です: Chrome 122 (2024年3月)、Firefox 131 (2024年9月)、Safari 18.2 (2024年12月)、Node.js 22。これらのバージョンより前の環境の場合、core-js 3.38+にpolyfillが含まれており、バンドルに追加できます。

Generator関数はすでにiteratorを返します — 今日Generatorを書いている場合、ターゲットが十分にモダンであれば、その戻り値はこれらのメソッドを自動的に獲得します。

🎮 自分で試してみる

▶️ インタラクティブプレイグラウンドを開く →

ブラウザ内で直接実行できます — 触ってコンセプトのライブ反応を見てみましょう。

🧠 自分でテストする

理解できましたか? 8問のクイズに挑戦する →

即時フィードバック、すべての質問にヒント、各回答の説明 — 正解・不正解ともに。

要点

コードベースでArrayに対する.filter().map().slice(0, n)チェーンを検索してください。それぞれがガベージコレクタに直行する中間配列を割り当てます。それらをIterator.from().take(n)に置き換えると、パイプラインは必要なものが得られ次第停止します — 余分な作業も、余分なメモリもありません。APIサーフェスはArrayメソッドとほぼ同一です。唯一の変化は、チェーンを変換するコレクションではなく、記述するパイプラインとして考えることです。


読んでいただきありがとうございます! つながりを保ちましょう: