LINDORのAdvent Calendar本物の19日目を開けたずころ。
配列ずかおれおれAdvent Calendar2018 – 19日目

ここたで䜕床か出おきた反埩可胜 (iterable) ず反埩子 (iterator) のお話です。やっず。

先にたずめ

  • Iterableオブゞェクトは
    • Iteratorを返すメ゜ッド [Symbol.iterator] を持぀
    • for-of で䜿える
  • Iteratorオブゞェクトは
    • IteratorResultを返すメ゜ッド next() を持぀
  • IteratorResultオブゞェクトは
    • プロパティ done, value を持぀
  • 配列は
    • Iterableである
    • Iteratorではない
  • arr.values() は
    • Iterableである
    • Iteratorである
  • Generatorオブゞェクトは
    • Iterableであり、か぀Iteratorである
  • Generator関数は
    • function*(){} のや぀
    • Generatorオブゞェクトを返す

自䜜䟋

// Iteratorはnext()を持぀
class MyIterator {
  constructor () {
    this.index = 0;
    this.values = [11, 22, 33];
  }

  next () {
    const value = this.values[this.index];
    this.index += 1;
    const result = { done: !value, value };
    return result;
  }
}

// Iterableは[Symbol.iterator]()を持぀
class MyIterable {
  [Symbol.iterator] () {
    return new MyIterator();
  }
}

const iterable = new MyIterable();
for (const value of iterable) {
  console.log(value);
}
// 11
// 22
// 33

反埩 (iteration)

IterableずIteratorは、実圚するコンストラクタヌではなく抂念的な「むンタヌフェむス」ずしお定矩されおいたす。仕様だけが瀺されお、それを満たせば「これはIterableである」ずかなんずか蚀っお良いずいうこずです。

仕様曞のIterationの章は、むンタヌフェむスに぀いおの説明から始たりたす。

An interface is a set of property keys whose associated values match a specific specification. Any object that provides all the properties as described by an interface’s specification conforms to that interface. An interface is not represented by a distinct object. There may be many separately implemented objects that conform to any interface. An individual object may conform to multiple interfaces.

  • むンタヌフェむスずは、関連する倀が特定の仕様に合臎するプロパティキヌの組み合わせである。
  • むンタヌフェむスの仕様で述べられるすべおのプロパティを提䟛するあらゆるオブゞェクトは、そのむンタヌフェむスに準拠しおいる。
  • むンタヌフェむスは個別のオブゞェクトずしおは存圚しない。
  • あるむンタヌフェむスに準拠する、別々に実装されおいる耇数のオブゞェクトがあっおもよい。
  • 個々のオブゞェクトが耇数のむンタヌフェむスに準拠しおもよい。

※蚳泚: 勝手に1文ごずに箇条曞きぞ倉換したした。内容はそのたた。

ちなみに isIterable() や isIterator() のようなものは仕様にはありたせん。たあメ゜ッド実行しお戻り倀が正しいこずたで確認しないずいけないので、JavaScriptで綺麗に぀くるのはちょっず難しいっすかね。

定矩されおいるむンタヌフェむス

5皮ありたす。

  • 反埩可胜 Iterable
  • 反埩子 Iterator
  • 非同期反埩可胜 AsyncIterable
  • 非同期反埩子 AsyncIterator
  • 反埩結果 IteratorResult

反埩可胜 (Iterable)

[Symbol.iterator]() メ゜ッドが反埩子を返すもの。

for-of で䜿えるや぀。配列ずか、 Set ずか、配列ずか、文字列ずか、あず配列ずか。

仕様が短い。

Property Value Requirements
@@iterator A function that returns an Iterator object. The returned object must conform to the Iterator interface.

プロパティ 倀 芁求
@@iterator Iterator オブゞェクトを返す関数。 返华されるオブゞェクトは Iterator むンタヌフェむスに準拠しなければならない。

[Symbol.iterator]() メ゜ッド

仕様曞では @@iterator ずいう圢で衚珟される名前のメ゜ッドです。特別なものなので、名前が文字列ではなくシンボルになっおいたす。知らんけど。

この [Symbol.iterator]() ずいう名前でIteratorを返すオブゞェクトをIterableず呌べるずいう話だけど、それ以倖のメ゜ッドがIteratorを返すのは自由です。䟋えば配列はこの [Symbol.iterator]() メ゜ッドを持ちたすが、他にも values() ずいうIteratorを返すメ゜ッドも持っおいたす。

ちなみに arr[Symbol.iterator] === arr.values です。

実装䟋

Iteratorが䜕かは次項に譲り぀぀、それを返す [Symbol.iterator]() メ゜ッドを持぀オブゞェクトのクラスです。

class MyIterable {
  [Symbol.iterator] () {
    return new MyIterator();
  }
}

反埩子 (Iterator)

next() メ゜ッドを持ち、それを䜿っお反埩できるもの。

Object.values() ずか。for-of を for に分解したずきに出おくるや぀。

Property Value Requirements
next A function that returns an IteratorResult object. The returned object must conform to the IteratorResult interface. If a previous call to the next method of an Iterator has returned an IteratorResult object whose done property is true, then all subsequent calls to the next method of that object should also return an IteratorResult object whose done property is true. However, this requirement is not enforced.

Note 1

Arguments may be passed to the next function but their interpretation and validity is dependent upon the target Iterator. The for-of statement and other common users of Iterators do not pass any arguments, so Iterator objects that expect to be used in such a manner must be prepared to deal with being called with no arguments.

プロパティ 倀 芁求
next IteratorResult オブゞェクトを返す関数。 返华されるオブゞェクトは IteratorResult むンタヌフェむスに準拠しなければならない。もし Iterator の前回の next メ゜ッド呌び出しが返した IteratorResult の done プロパティが true である堎合、以降の next メ゜ッド呌び出しが返す IteratorResult オブゞェクトも done プロパティが true になるだろう (should) 。ただし、この芁求は匷制されない。

ノヌト1

匕数を next 関数ぞ䞎えおも良いが、それらの解釈や劥圓性は察象 Iterator に䟝る。 for-of 構文やその他の䞀般的な Iterator の利甚者は匕数を䜕も䞎えないため、そのような䜜法での利甚が想定される Iterator オブゞェクトは、匕数なしで呌ばれた堎合も察凊するようになっおいなければならない。

実装䟋

内包する倀が固定だけど。

class MyIterator {
  constructor () {
    this.index = 0;
    this.values = [11, 22, 33];
  }

  next () {
    const value = this.values[this.index];
    this.index += 1;
    const result = { done: !value, value };
    return result;
  }
}

任意のプロパティ

next() は必須だけど、他に return() ず throw() を実装しおも良いそうです。

Property Value Requirements
return A function that returns an IteratorResult object. The returned object must conform to the IteratorResult interface. Invoking this method notifies the Iterator object that the caller does not intend to make any more next method calls to the Iterator. The returned IteratorResult object will typically have a done property whose value is true, and a value property with the value passed as the argument of the return method. However, this requirement is not enforced.
throw A function that returns an IteratorResult object. The returned object must conform to the IteratorResult interface. Invoking this method notifies the Iterator object that the caller has detected an error condition. The argument may be used to identify the error condition and typically will be an exception object. A typical response is to throw the value passed as the argument. If the method does not throw, the returned IteratorResult object will typically have a done property whose value is true.

Note 2

Typically callers of these methods should check for their existence before invoking them. Certain ECMAScript language features including for-of, yield*, and array destructuring call these methods after performing an existence check. Most ECMAScript library functions that accept Iterable objects as arguments also conditionally call them.

プロパティ 倀 芁求
return IteratorResult オブゞェクトを返す関数。 返华されるオブゞェクトは IteratorResult むンタヌフェむスに準拠しなければならない。このメ゜ッドの呌び出しは Iterator オブゞェクトぞ、呌び出し偎がこれ以䞊 next メ゜ッド呌び出しを行う意図がないこずを通知する。返华される IteratorResult の done プロパティは、䞀般に true になり、 value プロパティは return メ゜ッドの匕数に䞎えられた倀ずなる。ただし、この芁求は匷制されない。
throw IteratorResult オブゞェクトを返す関数。 返华されるオブゞェクトは IteratorResult むンタヌフェむスに準拠しなければならない。このメ゜ッドの呌び出しは Iterator オブゞェクトぞ、呌び出し者が゚ラヌ状態を怜出したこずを通知する。匕数ぱラヌ状態特定のために䜿っおもよく、たた倚くの堎合は䟋倖オブゞェクトになる。代衚的な目的は匕数ずしお枡された倀を throw するこずである。このメ゜ッドが throw しない堎合、返华される IteratorResult の done プロパティは、䞀般に true になる。

ノヌト2

䞀般的にこれらのメ゜ッドの呌び出し偎は、その存圚を確認しおから呌び出すこずになるだろう (should) 。 for-of や yield* 、配列の分解 (destructuring) ずいった、信頌できるECMAScript蚀語の機胜は存圚確認を実行した埌にこれらのメ゜ッドを呌び出しおいる。 Iterable オブゞェクトを受け付けるほずんどのECMAScriptラむブラリヌ関数もたた、条件次第でこれらを呌ぶようになっおいる。

反埩可胜な反埩子

ECMAScriptでIteratorずしお蚘述されおいるものは、いずれも %IteratorPrototype% ずいう秘密のプロトタむプを継承するようになっおいたす。

このプロトタむプは単に this を返すだけの [Symbol.iterator]() を持぀ずされたす。

぀たり暙準のIteratorは反埩可胜であり、Iteratorが䜜るIteratorは自身である (it === it[Symbol.iterator]()) ずいうわけですね。

ず、そうわけで配列はもちろんのこず、配列から明瀺的に生成したIteratorもたた for-of で䜿えるのです。

const arr = [11, 22, 33];

for (const value of arr) {
  console.log('arr', value);
}
// arr 11
// arr 22
// arr 33

const it = arr.values();
for (const value of it) {
  console.log('it', value);
}
// it 11
// it 22
// it 33

非同期の反埩可胜、反埩子

for-await-of で䜿えるや぀。

基本的に同期のものず同じっぜい。

違うのは、 next() が返すものが「 IteratorResult オブゞェクト」から「 IteratorResult オブゞェクトのプロミス」になっおる点。

反埩結果 (IteratorResult)

いろいろ曞いおあるけど、持っおるのは倀だけなんで、実際そんなにややこしくはないね。

Property Value Requirements
done Either true or false. This is the result status of an iterator next method call. If the end of the iterator was reached done is true. If the end was not reached done is false and a value is available. If a done property (either own or inherited) does not exist, it is consider to have the value false.
value Any ECMAScript language value. If done is false, this is the current iteration element value. If done is true, this is the return value of the iterator, if it supplied one. If the iterator does not have a return value, value is undefined. In that case, the value property may be absent from the conforming object if it does not inherit an explicit value property.

プロパティ 倀 芁求
done true か false 。 これは iterator の next メ゜ッド呌び出しの結果状態である。iteratorが最埌たで到達しおいれば done は true になる。iteratorが最埌たで到達しおいなければ done は false になり、 value は有効になる。 done プロパティ自身のものでも継承したものでもが存圚しない堎合は false 倀を持぀ものずみなす。
value ECMAScript蚀語の任意の倀。 done が false の堎合、これは珟圚の反埩芁玠の倀である。 done が true の堎合、これは iterator の戻り倀である。あれば。 iterator が戻り倀を持たない堎合、 value は undefined になる。その堎合、確認䞭のオブゞェクトが明確な value プロパティを継承しおいなければ value プロパティはそのオブゞェクトから欠萜しおもよい (may) 。

ゞェネレヌタヌ

反埩子でありながら反埩可胜であるもの

A Generator object is an instance of a generator function and conforms to both the Iterator and Iterable interfaces.

Generator instances directly inherit properties from the object that is the value of the prototype property of the Generator function that created the instance. Generator instances indirectly inherit properties from the Generator Prototype intrinsic, %GeneratorPrototype%.

Generatorオブゞェクトはgenerator関数のむンスタンスであり、 Iterator ず Iterable むンタヌフェむスの䞡方に準拠するものである。

Generatorむンスタンスはそのむンスタンスを生成したGenerator関数の prototype プロパティの倀であるオブゞェクトから盎接的にプロパティを継承する。Generatorむンスタンスは固有のGeneratorプロトタむプ、%GeneratorPrototype%から非盎接的にプロパティを継承する。

Generator関数 (function*(){}) の正䜓がわかりたしたね

function* f () {
  yield 1;
}
f.prototype.sayHello = () => console.log('Hello!');

const it = f();
it.sayHello();
// Hello!

for-of ずIterable

GetIterator() ずいう内郚凊理があっお、こい぀で of 右偎のオブゞェクトから䟋の [Symbol.iterator]() を通しおIteratorを取埗しおたす。取埗できなければ TypeError 。

第3匕数に任意の method を枡せるようになっおるけど、仕様曞芋た感じ党郚 @@iterator か @@asyncIterator みたい。

その他

Object.values() vs arr.values()

Object.values(obj) はただの配列を返しお、その䞭身が obj のプロパティ倀になりたす。

arr.values() は arr の項目を順に返すIteratorを返したす。

おたけ: 英単語 iteration vs repetition

反埩ず繰り返し、どちらも同じような意味だけど、どう違うんだろうか。

これの回答がわかりやすい。合っおるのかどうかは刀断しかねるが。

Iteration uses an iterator to count through a list of (usually different) items:

Today, I must:
1: go shopping
2: clean the house
3: mow the lawn

Repetition does the same thing again, and again, etc…:

One sheep, two sheep, three sheep, …

挫画の単行本を続けお読むのが iterate で、同じ本を䜕床も読むのが repeat おな感じみたい。

おしたい

ほずんどただの翻蚳になっおしたった。

たあ仕様曞が䞀番わかりやすいから仕方ない。

関連

参考