Generator comprehensions

非標準。使用しないでください!
ジェネレーター内包は非標準であり、ECMAScript に追加されることはないでしょう。将来向きの用途には ジェネレーター の使用を検討してください。

generator comprehension 構文はすぐに既存の反復可能なオブジェクトに基づいて新たなジェネレータ関数を組み立てることができJavaScriptの式です。内包表記は、多くのプログラミング言語に存在しています。

ECMAScript第4版への提案に基づいたSpiderMonkeyでの古いジェネレータ式構文との違いについては、下記をご覧ください。

構文

(for (x of iterable) x)
(for (x of iterable) if (condition) x)
(for (x of iterable) for (y of iterable) x + y)

説明

ジェネレータ内包表記の中では、この2種類のコンポーネントが許可されています。:

for-ofイテレーションは常に最初のコンポーネントです。複数のfor-of イテレーションまたはif文が許可されています。

配列内包 の重大な欠点は、メモリー内に新しい配列全体を構築してしまうことです。配列内包への入力自体が小さい配列であるときのオーバーヘッドは小さいのですが、入力が大きな配列や処理の多い (あるいは本当に無限の) ジェネレーターであるときの配列の新規作成は問題になる場合があります。

ジェネレーターはアイテムを必要なときに要求に応じて算出するため、一連のデータの計算処理を軽減します。ジェネレーター内包は構文的に、配列内包とほとんど同じです。こちらは中括弧の代わりに丸括弧を使用して、配列を構築する代わりに、すぐには実行されないジェネレーターを作成します。これらは、ジェネレーター作成を簡略化した構文と考えることができます。

整数の大規模な数列に対して反復処理を行うイテレーター it を想定します。数列の値を 2 倍にする反復処理を行う、新たなイテレーターを作成したいとします。配列内包では、2 倍の値を含むのに十分な配列をメモリー内に作成します:

var doubles = [for (i in it) i * 2];

一方ジェネレーター内包は、必要なときに要求に応じて 2 倍の値を生成するイテレーターを作成します:

var it2 = (for (i in it) i * 2);
console.log(it2.next()); // The first value from it, doubled
console.log(it2.next()); // The second value from it, doubled

ジェネレーター内包が関数の引数として使用されるときは、関数の呼び出しで使用される丸括弧によりジェネレーター内包の外側の丸括弧を省略できます:

var result = doSomething(for (i in it) i * 2);

2 つの例の大きな違いは、ジェネレーター内包を使用すると 'obj' 構造を合計 1 回しかループする必要がないのと対照的に、配列内包ではイテレートの際に再びループすることです。

簡単なジェネレータ内包表記

(for (i of [ 1, 2, 3 ]) i*i );
// generator function which yields 1, 4, and 9

[...(for (i of [ 1, 2, 3 ]) i*i )];
// [1, 4, 9]

var abc = [ "A", "B", "C" ];
(for (letters of abc) letters.toLowerCase());
// generator function which yields "a", "b", and "c"

if文と用いたジェネレータ内包表記

var years = [ 1954, 1974, 1990, 2006, 2010, 2014 ];

(for (year of years) if (year > 2000) year);
// generator function which yields 2006, 2010, and 2014

(for (year of years) if (year > 2000) if(year < 2010) year);
// generator function which yields 2006, the same as below:

(for (year of years) if (year > 2000 && year < 2010) year);
// generator function which yields 2006

ジェネレータ関数と比較したジェネレータ内包表記

ジェネレータ内包表記構文を理解する簡単な方法はジェネレータ関数と比較することです。

例 1: 簡単なジェネレータ

var numbers = [ 1, 2, 3 ];

// Generator function
(function*() {
  for (let i of numbers) {
    yield i * i;
  }
})()

// Generator comprehension
(for (i of numbers) i*i );

// Result: both return a generator which yields [ 1, 4, 9 ]

例 2: ジェネレータ内でifを使用する

var numbers = [ 1, 2, 3 ];

// Generator function
(function*() {
  for (let i of numbers) {
    if (i < 3) {
      yield i * 1;
    }
  }
})()

// Generator comprehension
(for (i of numbers) if (i < 3) i);

// Result: both return a generator which yields [ 1, 2 ]

仕様

ジェネレーター内包は、ECMAScript 2015 で初期化されましたが、リビジョン 27 (2014 年 8 月) で取り除かれました。仕様セマンティクスについて、ES2015 の古いリビジョンをご覧ください。

ブラウザ実装状況

機能 Chrome Firefox (Gecko) Internet Explorer Opera Safari
基本サポート 未サポート 30 (30) 未サポート 未サポート 未サポート
機能 Android Chrome for Android Firefox Mobile (Gecko) IE Mobile Opera Mobile Safari Mobile
基本サポート 未サポート 未サポート 30.0 (30) 未サポート 未サポート 未サポート

SpiderMonkey固有の実装メモ

  • letは現在JS バージョン 1.7とXULスクリプトタグのみ利用可能ですので、識別子としてのlet はサポートされていません。
  • 内包表記での構造化代入はまだサポートされていません(バグ 980828)。

古いJS1.7/JS1.8内包表記との違い

JS1.7/JS1.8 の内包表記は、バージョン 46 で削除しました (バグ 1220564)。

古い内包表記の構文 (使用しないでください!):

(X for (Y in Z))
(X for each (Y in Z))
(X for (Y of Z))

違い:

  • ESNext の内包表記は全体のかわりに"for"ノードごとに1スコープを生成します。
    • 旧: [...(()=>x for (x of [0, 1, 2]))][1]() // 2
    • 新: [...(for (x of [0, 1, 2]) ()=>x)][1]() // 1, each iteration creates a fresh binding for x.
  • ESNext の内包表記は代入式のかわりに"for"で始まります。
    • 旧: (i * 2 for (i of numbers))
    • 新: (for (i of numbers) i * 2)
  • ESNext の内包表記は複数のifforコンポーネントを持ちます。
  • ESNext の内包表記はfor...ofでのみ動作し、for...inイテレーションでは動作しません。

関連情報

ドキュメントのタグと貢献者

 このページの貢献者: yyss, shide55
 最終更新者: yyss,