JavaScriptにおけるイテレーターパターンの実装と応用

イテレーターパターンは、集合オブジェクト内の要素を順番にアクセスするための方法を提供しつつ、その内部構造を隠蔽する設計パターンです。このパターンにより、反復処理のロジックをアプリケーションの本質的な処理から分離でき、データ構造の詳細に依存せずに要素を走査することが可能になります。

以下は、配列に対して繰り返し処理を行うeach関数の実装例です。第一引数として配列を受け取り、第二引数には各要素に対する処理を定義するコールバック関数を渡します。

const each = function (collection, action) {
  for (let index = 0; index < collection.length; index++) {
    const item = collection[index];
    action(item, index);
  }
};

each([1, 2, 3], (value, idx) => {
  console.log(`値: ${value}, インデックス: ${idx}`);
});

このeach関数は内部イテレータの一種であり、反復の流れを関数内に完全に封印しています。外部からは単一の呼び出しで済み、使い勝手が良い一方で、制御の自由度が制限されます。たとえば、2つの配列の要素が完全に一致するかを判定するような要件に対応する場合、内部イテレータではコールバック内で終了条件を明示的に処理する必要があります。

const compareArrays = function (arrA, arrB) {
  if (arrA.length !== arrB.length) {
    console.log('配列の長さが異なります');
    return;
  }

  each(arrA, (val, idx) => {
    if (val !== arrB[idx]) {
      console.log('要素が一致しません');
      return;
    }
  });
  console.log('配列は等しいです');
};

compareArrays([1, 2, 3, 4], [1, 2, 2, 3]);

このようなケースでは、処理の途中で中断したい場合でも、内部イテレータでは戻り値や例外による制御が難しく、コードの可読性が低下します。

これに対して、外部イテレータは反復の制御を呼び出し側に委ねる仕組みです。イテレータ自身が状態(現在のインデックス)を保持し、`next()`で次の要素へ進み、`isDone()`で終了判定を行い、`current()`で現在の値を取得できます。

const createIterator = function (collection) {
  let currentIndex = 0;

  return {
    next: () => {
      currentIndex += 1;
    },
    isDone: () => {
      return currentIndex >= collection.length;
    },
    current: () => {
      return collection[currentIndex];
    },
    length: collection.length
  };
};

外部イテレータは使用がやや複雑ですが、柔軟な制御が可能で、さまざまなシナリオに適応できます。特に、複数の反復条件や中途終了が必要な場面で威力を発揮します。

さらに、反復処理の終了条件をより明確に表現するために、`every`関数のような高階関数も実装できます。これはすべての要素が指定された条件を満たすかをチェックし、一つでも条件を満たさない場合、即座に終了します。

const every = function (collection, predicate) {
  for (let i = 0; i < collection.length; i++) {
    if (!predicate(collection[i], i)) {
      return false;
    }
  }
  return true;
};

const verifyEqual = function (arrA, arrB) {
  if (arrA.length !== arrB.length) {
    console.log('長さが異なるため不一致');
    return false;
  }

  const result = every(arrA, (val, idx) => val === arrB[idx]);
  if (result) {
    console.log('両配列は一致しています');
  } else {
    console.log('一部の要素が一致しません');
  }
  return result;
};

verifyEqual([1, 2, 3, 4], [1, 2, 2, 3]);

このように、イテレーターパターンはシンプルながらも、コードの再利用性・可読性・拡張性を高める重要な技術です。配列だけでなく、長さを持つ類似配列のオブジェクト(例:DOMノードリストなど)にも適用可能です。反復の仕組みを明確に分離することで、ソフトウェアの設計品質が向上します。

タグ: javascript イテレーターパターン 高階関数 反復処理 デザインパターン

9月15日 02:13 投稿