JavaScriptでオブジェクトの各値に対して繰り返し同じ処理を実行するイテレーターとジェネレーターの使い方

イテレーターとは?

イテレーター(iterator)とは、内包されている複数の値に順番にアクセスできる仕組みを備えたオブジェクトのことです。

語源の「iterate」は「繰り返し」を意味しますた。イテレーターを持つオブジェクトを「イテラブルなオブジェクト」といいます。

やや、難解な用語解説になりましたが、「イテラブルなオブジェクト」は普段の開発で知らず知らず使っているはずです。

  • 配列
  • 文字列
  • Mapオブジェクト
  • Setオブジェクト

など、組み込みのオブジェクトは内部で「イテラブルなオブジェクト」になる様に設計されており、配列は「イテラブルなオブジェクト」の代表例です。

配列を含む、「イテラブルなオブジェクト」はfor...of構文で順番に項目を処理することが可能です。

Output
for…of 文
Pug
table.table
			tbody
				tr
					th for...of 文
					td#targetText
JavaScript
const targetText = document.getElementById("targetText");

const targetArray = ['User A', 'User B', 'User C'];

for( let value of targetArray) {
	let additionalElement = document.createElement("div");
	additionalElement.textContent = value;
	targetText.appendChild(additionalElement);
}

では、わざわざイテレーターを使って反復処理を行う場面はどこでしょうか?

実はほとんどありません。なぜなら、for...of文は「イテレーターの取得から反復処理」までを一括で行ってくれる物だからです。

行いたい処理の内容によってイテレーターを使った方がコードがシンプルになることもありますが、ほとんどの場合はfor...of文などの組み込み構文を使うのが賢いでしょう。

ただし、イテレーターの理屈を知っておくことは重要なので、組み込み構文に頼らずに書ける様にしておくことは大事です。

では、イテレーターの使い方を見ていきましょう。

イテレーターの使い方

メソッド

メソッド意味返り値
オブジェクト[Symbol.iterator]()イテレータを取得イテレーター
イテレーター.next()次のイテレータを取得イテレーター
イテレータメソッドの仕様

プロパティ

プロパティ意味返り値
イテレーター.value現在の値任意
イテレーター.done終了したかどうか真偽値
イテレータ関連のプロパティの仕様

解説

配列の個別のイテレーターにアクセスするには、下記の様なコードを書きます。

JavaScript
//配列の定義
const array = ['User A', 'User B', 'User C'];

//配列のイテレーターを取得
const iterator = array[Symbol.iterator]();

こうすることで、イテレーターを利用可能になります。

まずはは最初のイテレータを取得しましょう。この場合は「User A」の値ですね。next()メソッドを実行します。

次の様なコードになります。

JavaScript
//最初のイテレーターを取得
const next1 = iterator.next();

定数 next1には最初のイテレーターのオブジェクトが入りました。

オブジェクトのデータは下記の様になります。

JavaScript
{value: "User A", done: false}

valueとプロパティの値が入っているのが確認できます。

次のイテレーターに移動するには再びnext()メソッドを実行します。この様に繰り返し処理を行い、全ての値に対して処理を行っていきます。

最終的には下記の様な実装になります。

このサンプルコードはvalueとdoneの値をそのまま出力させたコードです。

Output
イテレーターの中身
Pug
table.table
			tbody
				tr
					th イテレーターの中身
					td#targetText2
JavaScript
const targetText2 = document.getElementById("targetText2");

const array = ['User A', 'User B', 'User C'];

const iterator = array[Symbol.iterator]();

const next1 = iterator.next();
let additionalElement1 = document.createElement("div");
additionalElement1.textContent = `${next1.value}:${next1.done}`;
targetText2.appendChild(additionalElement1);


const next2 = iterator.next();
let additionalElement2 = document.createElement("div");
additionalElement2.textContent = `${next2.value}:${next2.done}`;
targetText2.appendChild(additionalElement2);


const next3 = iterator.next();
let additionalElement3 = document.createElement("div");
additionalElement3.textContent = `${next3.value}:${next3.done}`;
targetText2.appendChild(additionalElement3);

const next4 = iterator.next();
let additionalElement4 = document.createElement("div");
additionalElement4.textContent = `${next4.value}:${next4.done}`;
targetText2.appendChild(additionalElement4);

これがイテレーターの仕組みです。

コードが非常に長ったらしいのがわかるでしょう。そのため、こういった処理を行うにはfor...of文などを利用するのが好ましいということになります。

イテラブルなオブジェクトの作り方(ジェネレーター)

「イテラブルなオブジェクト」は自作が可能です。この自作するときの機能を「ジェネレーター」といいます。

「ジェネレーター」を使うことで、配列や文字列という型を気にすることなく「イテレーター」を利用可能です。

ジェネレーターを使うことで、「イテラブルなオブジェクト」が作成可能なので、for…of文を適用することが可能になります。

KAZU
KAZU

イテレーターを使うなら私はfor…of文とか使うけど、ジェネレーターは割と使います。

構文

構文意味
function* 関数名() {}ジェネレーターを定義
yield 値値を返す
ジェネレーターの仕様

ジェネレーターを定義するには、次の様にfunction宣言に対してアスタリスク(*)を付与します。

解説

イテレーターでは、イテレーター.next()で次々に値を取得することができました。ジェネレーターは「次に何を取り出すか?」をyieldにとって返します。

KAZU
KAZU

yieldはジェネレーター文内におけるreturnと同じ働きを持つ物と捉えればわかりやすいでしょう。

サンプルコード

それでは、先ほど紹介したコードでは、配列のイテレーターにアクセスして処理を行いましたが、今回はそのイテレーターを自作して見ましょう。

書き換えたコードは下記の通りです。

Output
イテレーターの中身
Pug
table.table
			tbody
				tr
					th イテレーターの中身
					td#targetText3
JavaScript
const targetText3 = document.getElementById("targetText3");

function* targetGenerator() {
  yield 'User A';
  yield 'Uaser B';
  yield 'User C';
}
const targetIterable = targetGenerator();

const next1 = targetIterable.next();
let additionalElement1 = document.createElement("div");
additionalElement1.textContent = `${next1.value}:${next1.done}`;
targetText3.appendChild(additionalElement1);

const next2 = targetIterable.next();
let additionalElement2 = document.createElement("div");
additionalElement2.textContent = `${next2.value}:${next2.done}`;
targetText3.appendChild(additionalElement2);


const next3 = targetIterable.next();
let additionalElement3 = document.createElement("div");
additionalElement3.textContent = `${next3.value}:${next3.done}`;
targetText3.appendChild(additionalElement3);

const next4 = targetIterable.next();
let additionalElement4 = document.createElement("div");
additionalElement4.textContent = `${next4.value}:${next4.done}`;
targetText3.appendChild(additionalElement4);

もちろん、for…of文を使ってイテレーターの利用をもっとシンプルに書くことも可能です。

Output
イテレーターの中身
Pug
table.table
			tbody
				tr
					th イテレーターの中身
					td#targetText4
JavaScript
const targetText4 = document.getElementById("targetText4");

function* targetGenerator2() {
  yield 'User A';
  yield 'Uaser B';
  yield 'User C';
}
const targetIterable2 = targetGenerator2();


for (let value of targetIterable2) {
	let additionalElement = document.createElement("div");
	additionalElement.textContent = `${value}`;
	targetText4.appendChild(additionalElement);
}
インストラクター