イテレータを学びたい

みなさんJavaScriptでイテレータってますか?
僕はもっぱらジェネレータ派で(だってイテレータってなんかめんどくさいんだもん)、全然使ったことがありません。
ですがいつまでも毛嫌いしているわけにもいかんということで、使い方を勉強してみることにします。

イテレータとは

イテレータとは、繰り返しのための機構のことです。
これだけじゃナンノコッチャですね。
実はイテレータと一口に言っても、反復可能な(すなわちiterableな)オブジェクトと、イテレータオブジェクトの二種類があります。
例えば配列はiterableなオブジェクトではありますが、イテレータオブジェクトではありません。
また、iterableなオブジェクトでかつイテレータオブジェクトであることも可能です。
これはどういうことか?

簡単です。

iterableなオブジェクトの条件:for-ofで各要素にアクセスできるオブジェクト([Symbol.iterator]メソッドを実装しているオブジェクト)
イテレータオブジェクトの条件:nextメソッドを持ち、nextメソッドがvalueとdoneプロパティを持つオブジェクトを返すオブジェクト

詳しく見ていきましょう。

iterableなオブジェクト

先にも書いたように、iterableなオブジェクトとはfor-ofで各要素にアクセスできるオブジェクトのことを言います。
おそらく最も利用されているのは配列ではないかなーと思いますが、別に配列でなくてもかまいません。
例えば文字列もiterableなオブジェクトです。MAPもそうです。

const text = 'わいが天才や!';
for(let t of text){
console.log(t);//変数tで一文字ずつ文字を受け取り、それをコンソール画面に出力。
}

for-ofが利用可能かどうかというのは、[Symbol.iterator]メソッドを実装しているかどうかによって決まります。
確認してみましょう。

const proto = Object.getPrototypeOf(text);//まずプロトタイプを取得。
console.log(Object.getOwnPropertySymbols(proto));// -> [ Symbol(Symbol.iterator) ]

はいこれでStringクラスは[Symbol.iterator]メソッドを実装していることがわかりましたね。
同じように配列も[Symbol.iterator]メソッドを実装しています。
したがってfor-ofメソッドで回すことできます。

ちなみにですが、「[Symbol.iterator]メソッドを実装しているならfor-ofの利用が可能であるはずだ」と言い換えることもできます。
Aというオブジェクトがfor-ofで回すことができるのかどうかを動的に確認するには、[Symbol.iterator]メソッドの実装をチェックすればよいということになりますね(「Aという機能を実装しているならBであるはずだ」ということをダックタイピングって言うらしいですよ)。

イテレータオブジェクト

イテレータオブジェクトならnextメソッド(valueとdoneプロパティを持つオブジェクトを返すメソッド)を実装しているはずです。
Stringクラスのインスタンスは果たして実装しているのでしょうか。
試してみます。

const text = 'わいが天才や!';
console.log(text.next());//text.next is not a function

残念ながらnextメソッドは実装されていないようです。
ですがご安心ください。
iterableオブジェクトの場合、nextメソッドを使えるようにするのはわりと簡単です。

iterableオブジェクトは[Symbol.iterator]メソッドを実装しています。
そしてこの[Symbol.iterator]メソッドは、戻り値としてイテレータを返します
これを使ってやればよいのです。
具体的には以下のような感じになります。

const text = 'わいが天才や!';
const it = text[Symbol.iterator]();
const it2 = text[Symbol.iterator]();
for(let i = 0; i < 10; i++){ console.log(it.next()); } console.log(it2.next());
{ value: 'わ', done: false }
{ value: 'い', done: false }
{ value: 'が', done: false }
{ value: '天', done: false }
{ value: '才', done: false }
{ value: 'や', done: false }
{ value: '!', done: false }
{ value: undefined, done: true }
{ value: undefined, done: true }
{ value: undefined, done: true }
{ value: 'わ', done: false }

itとit2が別物であることがわかりますね。
また、最後の要素までチェックが終了するとdoneプロパティがtrueになっていることも確認できます。

自分でイテレータオブジェクトを作ってみる

最後に自分でiterableなイテレータオブジェクトCarsを作ってみました。

class Cars {
constructor() {
this.list = [];
this.count = 0;
}

add(name) {
this.list.push(name);
return this.list;
}

[Symbol.iterator]() {
return this.list[Symbol.iterator]();
}

next() {
return (this.list.length > this.count) ? {value: this.list[this.count++], done: false} : {value: undefined, done: true};
}
}

const test = new Cars();
console.log(test.add('くるま'));
console.log(test.add('かー'));
console.log(test.add('べんつ'));
console.log(test.add('じゃがあ'));

console.log('-----');

//for-ofのテスト
for(let car of test){
console.log(car);
}

console.log('-----');

//Carsクラスのインスタンスの[Symbol.iterator]メソッドが返すイテレータオブジェクトのチェック
let hoge = test[Symbol.iterator]();
let hoge2 = test[Symbol.iterator]();
console.log(hoge.next());
console.log(hoge.next());
console.log(hoge2.next());
console.log('-----');

//nextメソッドのテスト
for(let i = 0; i < 10; i++){ console.log(test.next()); } //hogeの続き console.log('-----'); console.log(hoge.next()); console.log(hoge.next()); console.log(hoge.next()); console.log(hoge2.next());

結果はこんな感じ。

[ 'くるま' ]
[ 'くるま', 'かー' ]
[ 'くるま', 'かー', 'べんつ' ]
[ 'くるま', 'かー', 'べんつ', 'じゃがあ' ]
-----
くるま
かー
べんつ
じゃがあ
-----
{ value: 'くるま', done: false }
{ value: 'かー', done: false }
{ value: 'くるま', done: false }
-----
{ value: 'くるま', done: false }
{ value: 'かー', done: false }
{ value: 'べんつ', done: false }
{ value: 'じゃがあ', done: false }
{ value: undefined, done: true }
{ value: undefined, done: true }
{ value: undefined, done: true }
{ value: undefined, done: true }
{ value: undefined, done: true }
{ value: undefined, done: true }
-----
{ value: 'べんつ', done: false }
{ value: 'じゃがあ', done: false }
{ value: undefined, done: true }
{ value: 'かー', done: false }

うまく動いてくれました。
でもこれnextメソッドいりますかね?w
クラスのインスタンスをfor-ofで回せると色々と便利そうなので[Symbol.iterator]メソッドの実装はかなり有効そうですが……。
まだまだこの辺は実践が足りず、うまい活用方法がわかりませんw

ほなこんなところで、また。

フォローする