JavaScript/Iterator Interface

カテゴリ:Book:JavaScript#Iterator%20Interface%20

Iterator インターフェースは、コレクションなどの反復可能なオブジェクトの要素を順番に取得するための標準的な方法を定義するものです。このインターフェースは、JavaScript の反復処理メカニズムの基盤となっています[1]

インターフェース定義

イテレータは以下のメソッドを実装する必要があります:

interface Iterator {
  next(): IteratorResult;
  return?(value?: any): IteratorResult;
  throw?(e?: any): IteratorResult;
}

interface IteratorResult {
  done: boolean;
  value: any;
}
  • next(): 次の反復ステップの結果を返します。
  • return() (オプション): イテレータを早期終了させるために呼び出されます。
  • throw() (オプション): イテレータにエラーを報告するために呼び出されます。

基本的なイテレータの実装

以下のプログラムは、配列の要素を反復処理する基本的なイテレータを実装しています。

function createArrayIterator(array) {
  let index = 0;
  
  return {
    next() {
      if (index < array.length) {
        return { value: array[index++], done: false };
      } else {
        return { value: undefined, done: true };
      }
    }
  };
}

const iterator = createArrayIterator([1, 2, 3]);
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: undefined, done: true }

このプログラムでは、配列の要素を順番に返すイテレータを作成しています。next() メソッドは、次の要素と反復が完了したかどうかを示す IteratorResult オブジェクトを返します。

return()メソッドを実装したイテレータ

以下のプログラムは、return() メソッドを実装したイテレータの例を示しています。

function createResourceIterator() {
  let counter = 0;
  let isReleased = false;
  
  // 仮想的なリソースを確保
  console.log("リソースを確保しました");
  
  return {
    next() {
      if (isReleased || counter >= 3) {
        return { done: true, value: undefined };
      }
      return { done: false, value: counter++ };
    },
    return() {
      if (!isReleased) {
        // 仮想的なリソースを解放
        console.log("リソースを解放しました");
        isReleased = true;
      }
      return { done: true, value: undefined };
    }
  };
}

const iterator = createResourceIterator();
console.log(iterator.next()); // { done: false, value: 0 }

// for...ofループの途中で中断するとreturn()が呼ばれる
for (const value of {
  [Symbol.iterator]() { return iterator; }
}) {
  console.log(`値: ${value}`);
  break; // ループを中断
}
// 出力:
// リソースを確保しました
// { done: false, value: 0 }
// 値: 1
// リソースを解放しました

このプログラムでは、リソースを管理するイテレータを作成し、return() メソッドを実装しています。for...of ループが中断されると、return() メソッドが自動的に呼び出され、リソースが解放されます。

throw()メソッドを実装したジェネレータ

以下のプログラムは、throw() メソッドの振る舞いをジェネレータで示しています。

function* generatorWithErrorHandling() {
  try {
    yield 1;
    yield 2;
    yield 3;
  } catch (e) {
    console.log(`エラーをキャッチしました: ${e}`);
    yield 'エラー後';
  }
}

const iterator = generatorWithErrorHandling();
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.throw('カスタムエラー')); // エラーをキャッチしました: カスタムエラー
                                             // { value: 'エラー後', done: false }
console.log(iterator.next()); // { value: undefined, done: true }

このプログラムでは、ジェネレータを使用して throw() メソッドの振る舞いを示しています。ジェネレータのイテレータに throw() メソッドを呼び出すと、ジェネレータ内の try/catch ブロックでエラーをキャッチできます。

注意点

  • 反復可能(Iterable)との関係: 反復可能なオブジェクトは Symbol.iterator メソッドを持ち、イテレータを返します。
  • 完了状態: イテレータが { done: true } を返した後、再度 next() を呼び出しても通常は { done: true, value: undefined } を返します。
  • オプショナルメソッド: return()throw() はオプショナルであり、すべてのイテレータで実装されているわけではありません。
  • ジェネレータとの関係: ジェネレータ関数はイテレータインターフェースを完全に実装したオブジェクトを返します。

脚註

  1. JavaScriptのfor...ofループやスプレッド構文などの反復構文はこのインターフェースに依存しています。

外部リンク

カテゴリ:Book:JavaScript#Iterator%20Interface%20 カテゴリ:JavaScript
カテゴリ:Book:JavaScript カテゴリ:JavaScript カテゴリ:Pages using the JsonConfig extension