JavaScript ES6では、イテラブルとイテレーターの違いは何ですか?


14

イテラブルはイテレーターと同じですか、それとも異なりますか?

と思われる仕様から、反復可能なオブジェクトである、と言う、objそのように、obj[Symbol.iterator]呼び出されたときに、あるオブジェクトを返すように、機能を指しnext返すことができます方法{value: ___, done: ___}:オブジェクトを

function foo() {
    let i = 0;
    const wah = {
        next: function() {
            if (i <= 2) return { value: (1 + 2 * i++), done: false }
            else return { value: undefined, done: true }
        }
    };
    return wah;     // wah is iterator
}

let bar = {}        // bar is iterable

bar[Symbol.iterator] = foo;

console.log([...bar]);             // [1, 3, 5]   
for (a of bar) console.log(a);     // 1 3 5 (in three lines)

したがって、上記のコードでbarは、は反復可能でありwah、反復子であり、next()は反復子インターフェースです。

したがって、イテラブルとイテレータは別物です。

ただし、ジェネレータとイテレータの一般的な例では次のようになります。

function* gen1() {
    yield 1;
    yield 3;
    yield 5;
}

const iter1 = gen1();

console.log([...iter1]);                           // [1, 3, 5]
for (a of iter1) console.log(a);                   // nothing

const iter2 = gen1();
for (a of iter2) console.log(a);                   // 1 3 5 (in three lines)

console.log(iter1[Symbol.iterator]() === iter1);   // true

上記の場合、gen1はジェネレーターでありiter1、イテレーターでありiter1.next()、適切な処理を行います。しかしiter1[Symbol.iterator]、呼び出されたときにiter1イテレータであるを返す関数を提供します。だから、iter1両方のは、この場合に反復可能とイテレータのですか?

加えて、iter1上記の例1とは異なります。これは、例1のイテラブルは[1, 3, 5]を使用して必要な回数だけ与えることができるの[...bar]に対し、iter1はイテラブルですが、それ自体が返されるため、毎回同じイテレータであるため、[1, 3, 5]1回しか与えられません。

つまり、反復可能であればbar、何回[...bar]結果を出すことができると言えるでしょう[1, 3, 5]か。答えは、状況によって異なります。イテレータはイテレータと同じですか?そして答えは、それらは異なるものですが、イテラブルがイテレータとしてそれ自体を使用する場合、それらは同じになる可能性があります。あれは正しいですか?



だから、iter1反復可能とイテレータの両方が、この場合にはある?」 -はい。すべてのネイティブイテレータは、自分自身を返すことでイテレート可能であるため、イテレート可能であることが期待される構成にそれらを簡単に渡すことができます。
Bergi

回答:


10

はい、イテラブルイテレータは異なりますが、ほとんどのイテレータ(keysまたはのvaluesメソッドから、またはArray.prototypeジェネレータ関数のジェネレータからなど、JavaScript自体から取得するすべてのイテレータを含む)は、次のようなメソッドを持つ%IteratorPrototype%オブジェクトから継承しますSymbol.iteratorこの:

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

その結果、すべての標準イテレータも反復可能です。直接使用するか、for-ofループなどで使用できます(反復子ではなく反復可能であることを期待しています)。

keys配列の方法を考えてみましょう。配列のキー(数値としてのインデックス)にアクセスする配列反復子を返します。イテレータを返すことに注意してください。しかし、それの一般的な使用法は次のとおりです。

for (const index of someArray.keys()) {
    // ...
}

for-ofイテレータではなくイテラブルを受け取りますが、なぜそれが機能するのですか?

イテレータも反復可能であるため機能します。Symbol.iteratorただ返しますthis

これが私の本の第6章で使用する例です。すべてのエントリをループしたいが最初のエントリはスキップsliceし、サブセットをスライスするのに使用したくない場合は、イテレータを取得して、最初の値を読み取ることができます。次にfor-ofループに渡します:

const a = ["one", "two", "three", "four"];
const it = a[Symbol.iterator]();
// Skip the first one
it.next();
// Loop through the rest
for (const value of it) {
    console.log(value);
}

これはすべて標準の反復子であることに注意してください。時々人々はこのように手動でコード化されたイテレータの例を示します:

rangeそこから返されるイテレータは反復可能ではないため、で使用しようとすると失敗しfor-ofます。

反復可能にするには、次のいずれかを行う必要があります。

  1. Symbol.iterator上記の回答の冒頭にメソッドを追加するか、
  2. そのメソッドをすでに持っている%IteratorPrototype%から継承する

悲しいことに、TC39は%IteratorPrototype%オブジェクトを直接取得する方法を提供しないことを決定しました。間接的な方法(配列からイテレータを取得し、そのプロトタイプ(%IteratorPrototype%と定義されている)を取得する)がありますが、それは面倒です。

とにかく、そのように手動でイテレータを書く必要はありません。それが返すジェネレータは反復可能であるため、ジェネレータ関数を使用してください:


対照的に、すべての反復可能オブジェクトが反復子であるとは限りません。配列は反復可能ですが、反復子ではありません。文字列、マップ、セットも同様です。


0

私は用語のより正確な定義がいくつかあることに気づきました、そしてこれらはより決定的な答えです:

よるES6仕様MDN

私たちが持っているとき

function* foo() {   // note the "*"
    yield 1;
    yield 3;
    yield 5;
}

fooジェネレータ関数と呼ばれます。そして私たちが持っているとき

let bar = foo();

barはジェネレータオブジェクトです。そして、ジェネレータオブジェクトは、反復可能なプロトコルと反復プロトコルの両方に準拠しています

単純なバージョンはイテレータインターフェースで、これは単なる .next()メソッドです。

反復可能なプロトコルは次のとおりです:オブジェクト objobj[Symbol.iterator]「オブジェクトを返し、反復子プロトコルに準拠するゼロ引数関数」を提供します。

によって MDNリンクのタイトル、また、我々はまた、単に「発電機」ジェネレータオブジェクトを呼び出すことができそうです。

に注意してください Nicolas Zakasの著書「ECMAScript 6を理解する」では、おそらく「ジェネレーター関数」を「ジェネレーター」、「ジェネレーターオブジェクト」を「イテレーター」と大まかに呼んでいる。要点は、これらは実際には両方とも「ジェネレータ」に関連している-1つはジェネレータ関数で、もう1つはジェネレータオブジェクトまたはジェネレータです。ジェネレーターオブジェクトは、反復可能なプロトコルと反復子プロトコルの両方に準拠しています。

単にイテレータプロトコルに準拠するオブジェクトの場合は、またはを使用できませんiterableに準拠するオブジェクトである必要があります[...iter]for (a of iter)プロトコルに。

そして、まだドラフト段階にある将来のJavaScript仕様に、新しいIteratorクラスあります。それは、次のような方法を含む、より大きなインターフェース有しforEachmapreduceなどの現在の配列インターフェース、および新しいものの、およびtake、とdrop。現在のイテレータは、nextインターフェースます。

元の質問に答える:イテレータとイテラブルの違いは何ですか、答えは次のとおりです:イテレータはインターフェースを持つオブジェクトで.next()あり、イテラブルは次のobjようなオブジェクトですobj[Symbol.iterator]呼び出されたときにゼロ引数関数を与えるができるイテレータを返します。

そして、ジェネレーターはイテラブルであり、イテレーターでもあります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.