JavaScript/GeneratorFunction

GeneratorFunction オブジェクト

はじめに

JavaScriptにおけるGeneratorFunctionオブジェクトは、ジェネレータ関数を生成するための特殊な関数コンストラクタです。ジェネレータ関数は、実行を一時停止して後で再開できる特殊な関数で、イテレータプロトコルを実装しています。

基本的な構文

GeneratorFunctionコンストラクタは次のように使用します:

const GeneratorFunction = Object.getPrototypeOf(function*(){}).constructor;

// 新しいジェネレータ関数を作成
const myGenerator = new GeneratorFunction(arg1, arg2, ..., functionBody);

一般的には、ジェネレータ関数はfunction*構文を使って定義しますが、GeneratorFunctionコンストラクタを使用すると実行時に動的にジェネレータ関数を生成できます。

ジェネレータ関数の基本

ジェネレータ関数は、function* キーワードを使用して定義します:

function* simpleGenerator() {
  yield 1;
  yield 2;
  yield 3;
}

const generator = simpleGenerator();
console.log(generator.next().value); // 1
console.log(generator.next().value); // 2
console.log(generator.next().value); // 3
console.log(generator.next().done);  // true

ジェネレータ関数の実行は、各yield式で一時停止します。next()メソッドが呼び出されると、次のyield式まで実行が継続されます。

GeneratorFunctionオブジェクトのプロパティとメソッド

GeneratorFunctionオブジェクトは以下の特性を持ちます:

// プロトタイプチェーン
GeneratorFunction.prototype === function*(){}.constructor.prototype; // true

// インスタンスの作成
const add = new GeneratorFunction('a', 'b', 'yield a; yield b; yield a + b;');
const iterator = add(5, 3);

console.log(iterator.next().value); // 5
console.log(iterator.next().value); // 3
console.log(iterator.next().value); // 8

ジェネレータ関数の特徴

ジェネレータ関数から返されるジェネレータオブジェクトは、イテレーターであり、イテラブルです:

function* countTo(n) {
  for (let i = 1; i <= n; i++) {
    yield i;
  }
}

// for...of ループで使用
for (const num of countTo(5)) {
  console.log(num); // 1, 2, 3, 4, 5が順番に出力される
}

// スプレッド構文で使用
const numbers = [...countTo(5)];
console.log(numbers); // [1, 2, 3, 4, 5]

ジェネレータ関数における値の送信

next()メソッドに値を渡すことで、ジェネレータ関数に値を送信できます:

function* twoWayCommunication() {
  const x = yield 'What is your name?';
  const y = yield `Hello, ${x}! How old are you?`;
  return `${x} is ${y} years old.`;
}

const conversation = twoWayCommunication();
console.log(conversation.next().value);       // 'What is your name?'
console.log(conversation.next('Alice').value); // 'Hello, Alice! How old are you?'
console.log(conversation.next(30).value);      // 'Alice is 30 years old.'

next()、throw()、return()メソッド

ジェネレータオブジェクトは、その実行を制御するための3つの主要なメソッドを提供します:

function* sample() {
  try {
    yield 1;
    yield 2;
    yield 3;
  } catch (e) {
    console.log('エラーが発生しました:', e);
    yield 'エラー後';
  } finally {
    yield 'クリーンアップ';
  }
}

const gen = sample();
console.log(gen.next().value);           // 1
console.log(gen.next().value);           // 2
console.log(gen.throw('カスタムエラー').value); // 'エラーが発生しました: カスタムエラー', 'エラー後'
console.log(gen.return('早期終了').value);   // 'クリーンアップ'

ジェネレータの合成

ジェネレータ関数は、yield*式を使用して他のジェネレータやイテラブルを委譲できます:

function* generateSequence() {
  yield 1;
  yield 2;
}

function* generatePasswordCodes() {
  yield* generateSequence();
  yield* [3, 4];
  yield* 'ABCD';
}

const passwordGenerator = generatePasswordCodes();
for (const value of passwordGenerator) {
  console.log(value); // 1, 2, 3, 4, 'A', 'B', 'C', 'D'
}

非同期プログラミングでの活用

ジェネレータ関数は、非同期プログラミングを簡略化するために使用できます:

function fetchData(url) {
  return new Promise((resolve) => {
    setTimeout(() => resolve(`データ from ${url}`), 1000);
  });
}

function* dataFlow() {
  const result1 = yield fetchData('/api/data1');
  console.log(result1);
  
  const result2 = yield fetchData('/api/data2');
  console.log(result2);
  
  return 'すべて完了';
}

function runGenerator(generator) {
  const iterator = generator();
  
  function handle(iteratorResult) {
    if (iteratorResult.done) {
      return iteratorResult.value;
    }
    
    const promise = iteratorResult.value;
    return promise.then(data => handle(iterator.next(data)));
  }
  
  return handle(iterator.next());
}

runGenerator(dataFlow).then(result => {
  console.log(result); // 'すべて完了'
});

ジェネレータ関数のパフォーマンス特性

ジェネレータ関数は、大きなデータセットの処理やストリーミング処理などのシナリオで特に有用です。メモリ使用量と実行時間の比較を以下の表にまとめました:

処理方法 メモリ使用量 処理時間 ユースケース
通常の配列 全データを保持 一度に全処理 小~中規模データ
ジェネレータ 現在の値のみ 必要に応じて処理 大規模データ、ストリーミング

まとめ

GeneratorFunctionオブジェクトとジェネレータ関数は、JavaScriptにおける強力なプログラミングツールです。イテレーション、遅延評価、非同期プログラミングなどの複雑なシナリオを扱うための優れた手段を提供します。特に、大量のデータを扱う場合や、複雑な状態管理が必要な場合に有効に活用できます。

附録

静的プロパティ

obj.length
obj.name
obj.prototype

静的アクセサ

静的メソッド

継承関係

GeneratorFunctionのインスタンスプロパティ

GeneratorFunction.prototype.length
GeneratorFunction.prototype.name
GeneratorFunction.prototype.prototype

GeneratorFunctionのインスタンスアクセサ

GeneratorFunctionのインスタンスメソッド

GeneratorFunctionのインスタンスプロパティ

GeneratorFunction.prototype.prototype
GeneratorFunction.prototype [ Symbol.toStringTag ]

GeneratorFunctionのインスタンスアクセサ

GeneratorFunctionのインスタンスメソッド

GeneratorFunction.prototype.constructor()

Functionのインスタンスプロパティ

Function.prototype.length
Function.prototype.name

Functionのインスタンスアクセサ

get Function.prototype.arguments
get Function.prototype.caller

Functionのインスタンスメソッド

Function.prototype.apply()
Function.prototype.bind()
Function.prototype.call()
Function.prototype.constructor()
Function.prototype.toString()
Function.prototype [ Symbol.hasInstance ] ()

Objectのインスタンスプロパティ

Objectのインスタンスアクセサ

get Object.prototype.__proto__

Objectのインスタンスメソッド

Object.prototype.__defineGetter__()
Object.prototype.__defineSetter__()
Object.prototype.__lookupGetter__()
Object.prototype.__lookupSetter__()
Object.prototype.constructor()
Object.prototype.hasOwnProperty()
Object.prototype.isPrototypeOf()
Object.prototype.propertyIsEnumerable()
Object.prototype.toLocaleString()
Object.prototype.toString()
Object.prototype.valueOf()
カテゴリ:JavaScript
カテゴリ:JavaScript