--- title: 反復処理プロトコル slug: Web/JavaScript/Reference/Iteration_protocols tags: - ECMAScript 2015 - Intermediate - Iterable - Iterator - JavaScript - ガイド - プロトコル - 中級者 - 反復処理 - 反復子 translation_of: Web/JavaScript/Reference/Iteration_protocols ---
ECMAScript 2015 で追加されたいくつかの機能の中で、反復処理プロトコルは新しい組み込みオブジェクトや構文ではなくプロトコルです。これらのプロトコルは以下のような単純な約束事によって、すべてのオブジェクトで実装することができます。
プロトコルは2つあります。 反復可能プロトコルと 反復子プロトコルです。
反復可能プロトコルによって、 JavaScript のオブジェクトは反復動作を定義またはカスタマイズすることができます。例えば、 {{jsxref("Statements/for...of", "for...of")}} 構造の中でどの値がループに使われるかです。一部の組み込み型は既定の反復動作を持つ組み込み反復可能オブジェクトで、これには {{jsxref("Array")}} や {{jsxref("Map")}} がありますが、他の型 ({{jsxref("Object")}} など) はそうではありません。
反復可能であるために、オブジェクトは @@iterator
メソッドを実装する必要があります。これはつまり、オブジェクト (または、プロトタイプチェーン上のオブジェクトの一つ) が {{jsxref("Symbol.iterator")}} 定数にて利用できる @@iterator
キーのプロパティを持つ必要があります。
プロパティ | 値 |
---|---|
[Symbol.iterator] |
反復子プロトコルに準拠するオブジェクトを返す、引数なしの関数。 |
({{jsxref("Statements/for...of", "for...of")}} ループの始まりのように) オブジェクトが反復される必要があるときはいつでも、その @@iterator
メソッドが引数なしで呼ばれます。そして、返される反復子は、反復される値を取得するために使用されます。
なお、この引数なしの関数が呼び出されると、反復可能オブジェクト上のメソッドとして呼び出されます。従って関数の中では、 this
キーワードを反復可能オブジェクトのプロパティにアクセスするために使用して、反復の間に何を提供するかを決めることができます。
この関数は普通の関数、またはジェネレーター関数にすることができ、そのため呼び出されると、反復子オブジェクトが返されます。このジェネレーター関数の中では yield
を使用してそれぞれの項目を提供することができます。
反復子プロトコルは、値のシーケンス (有限でも無限でも) を生成するための標準的な方法と、すべての値が生成された場合の返値を定義します。
以下の意味で next()
メソッドを実装していれば、オブジェクトは反復子になります。
プロパティ | 値 |
---|---|
next() |
引数なしの関数で、少なくとも以下の二つのプロパティを持つオブジェクトを返します。
|
メモ: 特定のオブジェクトが反復子プロトコルを実装しているかどうかを反射的に知ることはできません。しかし、反復子プロトコルと反復可能プロトコルの両方を満たすオブジェクトを作成するのは簡単です (以下の例にあるように)。
そうすることで、反復可能オブジェクトを期待するさまざまな構文で反復子を使用できます。したがって、反復子プロトコルを実装するには反復可能プロトコルも実装しないと、ほとんど役に立ちません。
// 反復子と反復可能の両プロトコルを満たす let myIterator = { next: function() { // ... }, [Symbol.iterator]: function() { return this; } };
{{jsxref("String")}} は組み込み反復可能オブジェクトの一例です。
let someString = 'hi'; console.log(typeof someString[Symbol.iterator]); // "function"
String
の{{jsxref("String/@@iterator", "既定の反復子", "", 1)}}は文字列のコードポイントを1つずつ返します。
let iterator = someString[Symbol.iterator](); console.log(iterator + ''); // "[object String Iterator]" console.log(iterator.next()); // { value: "h", done: false } console.log(iterator.next()); // { value: "i", done: false } console.log(iterator.next()); // { value: undefined, done: true }
一部の組み込みコンストラクター — 例えば{{jsxref("Operators/Spread_operator", "スプレッド構文", "", 1)}} — は、まったく同じ反復処理プロトコルを使用しています。
console.log([...someString]); // ["h", "i"]
自身の @@iterator
を提供することによって反復動作を再定義できます。:
// need to construct a String object explicitly to avoid auto-boxing let someString = new String('hi'); someString[Symbol.iterator] = function () { return { // this is the iterator object, returning a single element (the string "bye") next: function () { return this._first ? { value: 'bye', done: (this._first = false) } : { done: true } }, _first: true }; };
@@iterator
を再定義することによって、反復処理プロトコルを使用する組み込みコンストラクターの動作にどれほど影響を与えるか注意してください。
console.log([...someString]); // ["bye"] console.log(someString + ''); // "hi"
{{jsxref("String")}}, {{jsxref("Array")}}, {{jsxref("TypedArray")}}, {{jsxref("Map")}}, {{jsxref("Set")}} は、すべての組み込み反復可能オブジェクトです。というのも、それらすべてのプロトタイプオブジェクトは @@iterator
メソッドをもつからです。
下記のように反復可能オブジェクトを生成できます。
let myIterable = {}; myIterable[Symbol.iterator] = function* () { yield 1; yield 2; yield 3; }; console.log([...myIterable]); // [1, 2, 3]
反復可能オブジェクトを受け入れる API はたくさんあります。以下はその例です。
new Map([[1, 'a'], [2, 'b'], [3, 'c']]).get(2); // "b" let myObj = {}; new WeakMap([ [{}, 'a'], [myObj, 'b'], [{}, 'c'] ]).get(myObj); // "b" new Set([1, 2, 3]).has(3); // true new Set('123').has('2'); // true new WeakSet(function* () { yield {} yield myObj yield {} }()).has(myObj); // true
いくつかの文や式は反復可能オブジェクトを期待します。例えば、 {{jsxref("Statements/for...of", "for...of")}} ループ、{{jsxref("Operators/Spread_syntax", "スプレッド演算子", "", "1")}}、{{jsxref("Operators/yield*", "yield*")}}、{{jsxref("Operators/Destructuring_assignment", "分割代入", "", "1")}} などです。
for (let value of ['a', 'b', 'c']) { console.log(value); } // "a" // "b" // "c" console.log([...'abc']); // ["a", "b", "c"] function* gen() { yield* ['a', 'b', 'c']; } console.log(gen().next()); // { value: "a", done: false } [a, b, c] = new Set(['a', 'b', 'c']); console.log(a); // "a"
反復可能オブジェクトの @@iterator
メソッドが反復子オブジェクトを返さない場合、それは非整形反復可能オブジェクトと見なされます。
これを使用すると、ランタイムエラーやバグの挙動をもたらす可能性があります。
let nonWellFormedIterable = {} nonWellFormedIterable[Symbol.iterator] = () => 1 [...nonWellFormedIterable] // TypeError: [] is not a function
function makeIterator(array) { let nextIndex = 0 return { next: function() { return nextIndex < array.length ? { value: array[nextIndex++], done: false } : { done: true }; } }; } let it = makeIterator(['yo', 'ya']); console.log(it.next().value); // 'yo' console.log(it.next().value); // 'ya' console.log(it.next().done); // true
function idMaker() { let index = 0; return { next: function() { return { value: index++, done: false }; } }; } let it = idMaker(); console.log(it.next().value); // '0' console.log(it.next().value); // '1' console.log(it.next().value); // '2' // ...
function* makeSimpleGenerator(array) { let nextIndex = 0; while (nextIndex < array.length) { yield array[nextIndex++]; } } let gen = makeSimpleGenerator(['yo', 'ya']); console.log(gen.next().value); // 'yo' console.log(gen.next().value); // 'ya' console.log(gen.next().done); // true function* idMaker() { let index = 0; while (true) { yield index++; } } let gen = idMaker() console.log(gen.next().value); // '0' console.log(gen.next().value); // '1' console.log(gen.next().value); // '2' // ...
class SimpleClass { constructor(data) { this.data = data; } [Symbol.iterator]() { // Use a new index for each iterator. This makes multiple // iterations over the iterable safe for non-trivial cases, // such as use of break or nested looping over the same iterable. let index = 0; return { next: () => { if (index < this.data.length) { return {value: this.data[index++], done: false} } else { return {done: true} } } } } } const simple = new SimpleClass([1,2,3,4,5]); for (const val of simple) { console.log(val); // '1' '2' '3' '4' '5' }
{{jsxref("Generator", "ジェネレーターオブジェクト", "", 1)}} は、反復子でも反復可能でもあります。
let aGeneratorObject = function* () { yield 1; yield 2; yield 3; }(); console.log(typeof aGeneratorObject.next); // "function", because it has a next method, so it's an iterator console.log(typeof aGeneratorObject[Symbol.iterator]); // "function", because it has an @@iterator method, so it's an iterable console.log(aGeneratorObject[Symbol.iterator]() === aGeneratorObject); // true, because its @@iterator method returns itself (an iterator), so it's an well-formed iterable console.log([...aGeneratorObject]); // [1, 2, 3]
仕様書 |
---|
{{SpecName('ESDraft', '#sec-iteration', 'Iteration')}} |
function*
のドキュメント", "", 1)}}を参照してください。