1. 開発者向けのウェブ技術
  2. JavaScript
  3. JavaScript リファレンス
  4. 文と宣言
  5. function* 宣言

このページはコミュニティーの尽力で英語から翻訳されました。MDN Web Docs コミュニティーについてもっと知り、仲間になるにはこちらから。

View in English Always switch to English

function* 宣言

Baseline 広く利用可能

この機能は広く実装されており、多くのバージョンの端末やブラウザーで動作します。2016年9月以降、すべてのブラウザーで利用可能です。

function* 宣言は、新しいジェネレーター関数を指定された名前へのバインドとして作成します。ジェネレーター関数は、脱出した後でそのコンテキスト(変数のバインド)を保存したまま再入することが可能です。

ジェネレーター関数は function*を使って定義することもできます。

試してみましょう

function* generator(i) {
 yield i;
 yield i + 10;
}
const gen = generator(10);
console.log(gen.next().value);
// 予想される結果: 10
console.log(gen.next().value);
// 予想される結果: 20

構文

js
function* name(param0) {
 statements
}
function* name(param0, param1) {
 statements
}
function* name(param0, param1, /* ..., */ paramN) {
 statements
}

メモ: ジェネレーター関数には、対応するアロー関数はありません。

メモ: function* は別々なトークンなので、ホワイトスペースまたは改行で区切ることが可能です。

引数

name

関数名。

param 省略可

関数の形式上の引数の名前。引数の構文については、関数リファレンスを参照してください。

statements 省略可

関数の本体を構成する文。

解説

function* 宣言は GeneratorFunction オブジェクトを生成します。ジェネレーター関数が呼び出されるたびに、新しい Generator オブジェクトが返され、これはイテレータープロトコルに準拠します。ジェネレーター関数の実行は、ある場所で中断されます。初期状態では関数本体の先頭で中断されます。ジェネレーター関数は複数回呼び出して複数のジェネレーターを同時に生成できます。各ジェネレーターは、ジェネレーター関数の実行コンテキストを独自に保持し、独立してステップ実行できます。

ジェネレーターは双方向の制御フローをすることができます。制御フローはジェネレーター関数(呼び出し先)とその呼び出し側の間で、双方が望む回数だけ移行できます。制御フローは呼び出し側から呼び出し先へ、ジェネレーターのメソッド、next()throw()return() を呼んで移行します。制御フローは、returnthrow を使用して通常通り関数を終了させたり、すべての文を実行したり、yield および yield* 式を使用したりすることで、呼び出し側から呼び出し先へ進むことができます。

ジェネレーターの next() メソッドが呼び出されると、ジェネレーター関数の本体は次のいずれかになるまで実行されます。

  • yield 式。この場合、next() メソッドは、yield で返された値を含む value プロパティと、常に false である done プロパティを持つオブジェクトを返します。次に next() が呼び出されると、yield 式は next() に渡された値に評価されます。
  • 別のイテレーターに委譲する yield* 演算子。この場合、ジェネレーターに対するこの呼び出しおよび以降の next() 呼び出しは、委譲先のイテレーターが完了するまで、委譲先のイテレーターに対する next() 呼び出しと同等となります。
  • return 文(try...catch...finally で介入されないもの)、または制御フローの終わり(暗黙的に return undefined を意味します)。この場合、ジェネレーターは完了し、next() メソッドは返値を含む value プロパティと常に true となる done プロパティを持つオブジェクトを返します。以降の next() 呼び出しは効果を持たず、常に { value: undefined, done: true } を返します。
  • 関数内で発生したエラー(throw 文または未処理の例外による)。next()メソッドがこのエラーを発生させ、ジェネレータは完了する。以降のnext()呼び出しは効果なく、常に{ value: undefined, done: true }を返す。

ジェネレーターの throw() メソッドが呼び出されると、現在の中断位置でジェネレーターの本体に throw 文が挿入されたかのように動作します。同様に、ジェネレーターの return() メソッドが呼び出されると、現在の中断位置に return 文が挿入されたかのように動作します。どちらのメソッドも、ジェネレーター関数が try...catch...finally によって完了をキャッチしない限り、通常はジェネレーターを完了させます。

ジェネレーターはかつて非同期プログラミングのパラダイムであり、コールバック地獄制御の反転によって避けることができました。現在では、この場合の解決はよりシンプルな async 関数モデルと Promise オブジェクトで解決されています。しかし、ジェネレーターは依然として他の多くのタスク、例えばイテレーターを直感的に定義するといった用途で有用です。

function* 宣言は function 宣言と同様の挙動を示します。これらはスコープの先頭に巻き上げられ、そのスコープ内のどこでも呼び出せます。また、特定のコンテキストでのみ再宣言が可能です。

基本的な例

js
function* idMaker() {
 let index = 0;
 while (true) {
 yield index++;
 }
}
const gen = idMaker();
console.log(gen.next().value); // 0
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
console.log(gen.next().value); // 3
// ...

yield* を使用した例

js
function* anotherGenerator(i) {
 yield i + 1;
 yield i + 2;
 yield i + 3;
}
function* generator(i) {
 yield i;
 yield* anotherGenerator(i);
 yield i + 10;
}
const gen = generator(10);
console.log(gen.next().value); // 10
console.log(gen.next().value); // 11
console.log(gen.next().value); // 12
console.log(gen.next().value); // 13
console.log(gen.next().value); // 20

ジェネレーターに引数を渡す

js
function* logGenerator() {
 console.log(0);
 console.log(1, yield);
 console.log(2, yield);
 console.log(3, yield);
}
const gen = logGenerator();
// 最初の next の呼び出しで、関数の最初から、
// 最初の yield 文の前まで実行される。
gen.next(); // 0
gen.next("pretzel"); // 1 pretzel
gen.next("california"); // 2 california
gen.next("mayonnaise"); // 3 mayonnaise

ジェネレーターにおける return 文

js
function* yieldAndReturn() {
 yield "Y";
 return "R";
 yield "unreachable";
}
const gen = yieldAndReturn();
console.log(gen.next()); // { value: "Y", done: false }
console.log(gen.next()); // { value: "R", done: true }
console.log(gen.next()); // { value: undefined, done: true }

オブジェクトプロパティとしてのジェネレーター

js
const someObj = {
 *generator() {
 yield "a";
 yield "b";
 },
};
const gen = someObj.generator();
console.log(gen.next()); // { value: 'a', done: false }
console.log(gen.next()); // { value: 'b', done: false }
console.log(gen.next()); // { value: undefined, done: true }

オブジェクトメソッドとしてのジェネレーター

js
class Foo {
 *generator() {
 yield 1;
 yield 2;
 yield 3;
 }
}
const f = new Foo();
const gen = f.generator();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: false }
console.log(gen.next()); // { value: undefined, done: true }

計算プロパティとしてのジェネレーター

js
class Foo {
 *[Symbol.iterator]() {
 yield 1;
 yield 2;
 }
}
const SomeObj = {
 *[Symbol.iterator]() {
 yield "a";
 yield "b";
 },
};
console.log(Array.from(new Foo())); // [ 1, 2 ]
console.log(Array.from(SomeObj)); // [ 'a', 'b' ]

ジェネレーターはコンストラクターではない

js
function* f() {}
const obj = new f(); // throws "TypeError: f is not a constructor

ジェネレーターの例

js
function* powers(n) {
 // 生成の無限ループ
 for (let current = n; ; current *= n) {
 yield current;
 }
}
for (const power of powers(2)) {
 // ジェネレーターを制御
 if (power > 32) {
 break;
 }
 console.log(power);
 // 2
 // 4
 // 8
 // 16
 // 32
}

仕様書

仕様書
ECMAScript® 2027 Language Specification
# sec-generator-function-definitions

ブラウザーの互換性

関連情報

MDN の改良に協力

協力方法を知る

このページは MDN の貢献者によって に最終更新されました。

AltStyle によって変換されたページ (->オリジナル) /