Dalam JavaScript ES6, apa perbedaan antara iterable dan iterator?

14

Apakah iterable sama dengan iterator, atau mereka berbeda?

Tampaknya, dari spesifikasinya , iterable adalah objek, katakanlah, objsedemikian rupa yang obj[Symbol.iterator]merujuk ke fungsi, sehingga ketika dipanggil, mengembalikan objek yang memiliki nextmetode yang dapat mengembalikan {value: ___, done: ___}objek:

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)

Jadi dalam kode di atas, barapakah iterable, dan wahiterator, dan itu next()adalah antarmuka iterator.

Jadi, iterable dan iterator adalah hal yang berbeda.

Sekarang, dalam contoh umum generator dan iterator:

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

Dalam kasus di atas, gen1generator, dan iter1iterator, dan iter1.next()akan melakukan pekerjaan yang tepat. Tetapi iter1[Symbol.iterator]apakah memberikan fungsi itu, ketika dipanggil, memberikan kembali iter1, yang merupakan iterator. Jadi iter1apakah keduanya iterable dan iterator dalam kasus ini?

Selain itu, iter1berbeda dari contoh 1 di atas, karena iterable pada contoh 1 dapat memberikan [1, 3, 5]sebanyak yang diinginkan menggunakan [...bar], sementara iter1iterable, tetapi karena ia mengembalikan dirinya sendiri, yang merupakan iterator yang sama setiap kali, hanya akan memberikan [1, 3, 5]satu kali.

Jadi kita dapat mengatakan, untuk iterable bar, berapa kali dapat [...bar]memberikan hasil [1, 3, 5]- dan jawabannya adalah, itu tergantung. Dan apakah iterable sama dengan iterator? Dan jawabannya adalah, mereka adalah hal yang berbeda, tetapi mereka bisa sama, ketika iterable menggunakan dirinya sebagai iterator. Apakah itu benar?

nonopolaritas
sumber
" Jadi iter1apakah keduanya iterable dan iterator dalam kasus ini? " - ya. Semua iterator asli juga dapat diubah dengan mengembalikan diri mereka sendiri, sehingga Anda dapat dengan mudah mengirimkannya ke dalam konstruksi yang mengharapkan iterable.
Bergi
Kemungkinan duplikat Perbedaan antara Iterator dan Iterable
Felix Kling

Jawaban:

10

Ya, iterables dan iterator adalah hal yang berbeda, tetapi sebagian besar iterator (termasuk semua yang Anda dapatkan dari JavaScript itu sendiri, seperti dari keysatau valuesmetode pada Array.prototypeatau generator dari fungsi generator) mewarisi dari objek % IteratorPrototype% , yang memiliki Symbol.iteratormetode seperti ini:

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

Hasilnya adalah semua iterator standar juga dapat diubah. Itu sehingga Anda dapat menggunakannya secara langsung, atau menggunakannya dalam for-ofloop dan semacamnya (yang mengharapkan iterables, bukan iterator).

Pertimbangkan keysmetode array: Ini mengembalikan iterator array yang mengunjungi kunci array (indeksnya, sebagai angka). Perhatikan bahwa ia mengembalikan iterator . Tetapi penggunaan umum dari itu adalah:

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

for-ofmengambil iterable , bukan iterator , jadi mengapa itu berhasil?

Ini bekerja karena iterator juga dapat diubah; Symbol.iteratorbaru saja kembali this.

Berikut adalah contoh yang saya gunakan dalam Bab 6 buku saya: Jika Anda ingin mengulang semua entri tetapi melewati yang pertama dan Anda tidak ingin menggunakan sliceuntuk memotong subset, Anda bisa mendapatkan iterator, baca nilai pertama, lalu serahkan ke for-ofloop:

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);
}

Perhatikan bahwa ini semua iterator standar . Kadang-kadang orang menunjukkan contoh iterator yang dikodekan secara manual seperti ini:

Iterator dikembalikan oleh rangeada tidak sebuah iterable, sehingga gagal ketika kita mencoba untuk menggunakannya dengan for-of.

Untuk membuatnya lebih baik, kita harus:

  1. Tambahkan Symbol.iteratormetode di awal jawaban di atas, atau
  2. Buat warisan dari% IteratorPrototype%, yang sudah memiliki metode itu

Sayangnya, TC39 memutuskan untuk tidak memberikan cara langsung untuk mendapatkan objek% IteratorPrototype%. Ada cara tidak langsung (mendapatkan iterator dari array, lalu mengambil prototipe-nya, yang didefinisikan sebagai% IteratorPrototype%), tetapi itu menyebalkan.

Tapi toh tidak perlu menulis iterator secara manual seperti itu; cukup gunakan fungsi generator, karena generator yang dikembalikannya dapat diubah:


Sebaliknya, tidak semua iterables adalah iterator. Array bisa diubah, tetapi bukan iterator. Begitu juga string, Peta, dan Set.

TJ Crowder
sumber
0

Saya menemukan bahwa ada beberapa definisi istilah yang lebih tepat, dan ini adalah jawaban yang lebih pasti:

Menurut Spesifikasi ES6 dan MDN :

Kapan kita punya

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

foodisebut fungsi generator . Dan ketika kita punya

let bar = foo();

baradalah objek generator . Dan objek generator sesuai dengan protokol iterable dan protokol iterator .

Versi yang lebih sederhana adalah antarmuka iterator, yang hanya sebuah .next()metode.

Protokol iterable adalah: untuk objek obj, obj[Symbol.iterator]memberikan "fungsi argumen nol yang mengembalikan objek, sesuai dengan protokol iterator".

Dengan judul tautan MDN , sepertinya kita juga bisa menyebut objek generator sebagai "generator".

Perhatikan bahwa dalam buku Nicolas Zakas, Understanding ECMAScript 6 , ia mungkin secara longgar menyebut "fungsi generator" sebagai "generator", dan "objek generator" sebagai "iterator". Yang perlu diperhatikan adalah, keduanya benar-benar terkait "generator" - satu adalah fungsi generator, dan satu lagi adalah objek generator, atau generator. Objek generator sesuai dengan protokol iteratable dan protokol iterator.

Jika itu hanya objek yang sesuai dengan protokol iterator , Anda tidak dapat menggunakan [...iter]atau for (a of iter). Itu harus menjadi objek yang sesuai dengan protokol iterable .

Dan kemudian, ada juga kelas Iterator baru, dalam spesifikasi JavaScript masa depan yang masih dalam konsep . Memiliki antarmuka yang lebih besar, termasuk metode seperti forEach, map, reducedari antarmuka Array saat ini, dan yang baru, seperti dan take, dan drop. Iterator saat ini merujuk ke objek hanya dengan nextantarmuka.

Untuk menjawab pertanyaan awal: apa perbedaan antara iterator dan iterable, jawabannya adalah: iterator adalah objek dengan antarmuka .next(), dan iterable adalah objek objyang obj[Symbol.iterator]dapat memberikan fungsi argumen-nol yang, ketika dipanggil, mengembalikan iterator.

Dan generator adalah iterable dan iterator, untuk menambah itu.

nonopolaritas
sumber