🦔

JavaScript / イテレーター

2025/01/15に公開

イテレーターとは

反復処理が可能な(イテラブルな)オブジェクトがもつ、実際の反復処理を行う機能のこと。以下がイテラブルオブジェクトに挙げられる

イテラブルオブジェクト一覧

  1. 配列
  2. Map
  3. Set
  4. NodeList
  5. arguments オブジェクト
  6. 文字列(プリミティブ型だけど)

イテラブルオブジェクトの特徴の内、重要な2つ

1. for...of文で反復処理が可能

// for...of文の使用例
//**********************
// 配列の場合
const array = [1, 2, 3]
for (let item of array) {
  console.log(item);
  // > 1
  // > 2
  // > 3
};

// 文字列の場合
const str = "abc";
for (let item of str) {
  console.log(item);
  // > a
  // > b
  // > c
};

// オブジェクトの場合
const john = {
  age: 20,
  nationality: "US"
};

for (let details of john) {
  console.log(details);
  // > Uncaught TypeError: john is not iterable
  // > 訳)johnはイテラブルじゃねぇよ
};

2. (今回のミソ)Symbol.iteratorメソッドを持つ

というよりもfor...of文の反復処理は、内部的にSymbol.iteratorメソッドが返すオブジェクトの中のnextメソッドが行なっている。
人間がfor...of文を用いて反復処理をシンプルに書いている裏で、nextメソッドが頑張っている状態である。
冒頭でも説明したが、このような実際の反復処理を行う機能のことをイテレーターという。

直接イテレーターを使ってみる

1. イテラブルオブジェクトが持つSymbol.iteratorメソッドからオブジェクトを取得する。

const array = [1, 2, 3];
const iterator = array[Symbol.iterator]();

Symbol.iteratorを実行するとiteratorオブジェクトが返される。この中にnextメソッドが格納されている。
補足すると、Symbol.iteratorメソッドはSymbol型の組み込みオブジェクトのメソッド(ややこしい)である。symbol型がオブジェクトに格納されている場合、通常のプロパティのようにarray.Symbol.iterator()とアクセスすることはできない仕様になっている(文字列型としてオブジェクトのプロパティに設定されていないため)。アクセスするためにはブラケット記法を使おう。

Symbol型

(ES6)で導入されたプリミティブ型の一種。ユニークで不変な値を生成するために使用される。

  • 各Symbolは一意であり、同じ名前で作成されても異なる値となる
const sym1 = Symbol('hoge');
const sym2 = Symbol('hoge');
console.log(sym1 === sym2); // false
  • 一度作成されたSymbolは変更できない
  • オブジェクトのプロパティキーとして使用でき、他のキーと衝突しない

実際の開発で使用される例は、ほとんど無いが理由は今回は割愛

ブラケット記法

配列要素へのアクセス時のように[]でプロパティにアクセスする方法

2. iteratorの中を確認してみる

console.log(iterator);
// > Array Iterator {}
// > constructor : ƒ Iterator()
// > [[Prototype]] : Array Iterator
// > next : ƒ next()

目的のnextメソッドの取得が確認できた。

nextメソッドの使い方

nextを実行すると次のようなオブジェクトを返す。

{ value: <>, done: <true/false> }
  • value: 現在の値
  • done: 反復が終了したかどうかを示す(trueなら終了)

以下のように使用する。

const array = [1, 2, 3];
const iterator = array[Symbol.iterator]();

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 }

for...of文を使用している時、JavaScript内部ではnextメソッドがこのような処理を行なっている。

結局使い所はどこなのか

これに関しては実務で使用したことがないので、いまだにピンとこない・・・。
柔軟な反復処理を自作したい人は利用を検討する価値はあるらしい。

ただイテレータを学ぶ意義は大きいと思う。いかに理由を挙げるとしたら

  • 反復処理の内部動作への理解
  • 非イテラブルなオブジェクトでは使えない反復処理を利用することへのエラー回避
  • イテラブルなオブジェクトへの言語レベルの理解
  • Symbol型への理解

決して無駄になる知識ではないと思う。余力がある時にジェネレーターも勉強しよう。

Discussion