Iterasi mengacu pada proses mengekstraksi data secara terus menerus dari kumpulan data dalam urutan tertentu.
Jadi apa perbedaan antara iterasi dan traversal?
Dalam JavaScript, iterator adalah objek yang dapat memanggil metode next
untuk mengimplementasikan metode ini mengembalikan objek An dengan dua properti.
value
: Nilai berikutnya dari objek iterabledone
: Menunjukkan apakah semua data telah diambil. false
artinya masih ada data, true
artinya nanti tidak ada data.untuk menghasilkan iterator melalui fungsi pabrik iterator Symbol.iterator
di objek iterable.
const arr = []konsol.log(arr)
konstanta arr = [1, 2, 3] const iter1 = arr[Symbol.iterator]() // Buat iterator melalui fungsi pabrik iterator `Symbol.iterator`. konsol.log(iter1) konsol.log(iter1.next()) konsol.log(iter1.next()) konsol.log(iter1.next()) konsol.log(iter1.next()) console.log('%c%s', 'warna:merah;ukuran font:24px;', '=') const mymap = Peta baru() petaku.set('nama', 'clz') petaku.set('usia', 21) const iter2 = mymap[Symbol.iterator]() // Buat iterator melalui fungsi pabrik iterator `Symbol.iterator`. konsol.log(iter2) konsol.log(iter2.next()) konsol.log(iter2.next()) konsol.log(iter2.next())
Terlihat bahwa iterator selesai setelah mengambil nilai terakhir, yaitu ketika value
iterator berikutnya adalah undefined
.
Namun, pernyataan di atas tidak terlalu akurat. Pernyataan ini tidak selesai jika value
iterator berikutnya adalah undefined
. Anda juga perlu menentukan apakah benar-benar tidak ada nilai, atau apakah ada nilai dalam objek iterable yaitu undefined
. Jika ada nilai dalam objek iterable yang undefined
, maka nilai tersebut tidak akan selesai pada saat ini.
const arr = [1, 2, 3, tidak terdefinisi] const iter1 = arr[Symbol.iterator]() // Buat iterator melalui fungsi pabrik iterator `Symbol.iterator`. konsol.log(iter1) konsol.log(iter1.next()) konsol.log(iter1.next()) konsol.log(iter1.next()) konsol.log(iter1.next()) konsol.log(iter1.next())
dapat memanggil fungsi pabrik iterator beberapa kali tanpa mengganggu satu sama lain untuk menghasilkan beberapa iterator. Setiap iterator mewakili traversal terurut satu kali dari objek yang dapat diubah. Iterator yang berbeda tidak saling mengganggu dan hanya akan melintasi objek yang dapat diubah secara mandiri .
konstanta arr = [1, 2, 3] const iter1 = arr[Symbol.iterator]() // Buat iterator melalui fungsi pabrik iterator `Symbol.iterator`. const iter2 = arr[Simbol.iterator]() konsol.log('Iterator1:', iter1.next()) konsol.log('Iterator2:', iter2.next()) konsol.log('Iterator1:', iter1.next()) konsol.log('Iterator2:', iter2.next())
const arr = [1, 2, 3] const iter = arr[Simbol.iterator]() untuk (const i dari iter) { console.log(i) // Keluaran 1, 2, 3 secara berurutan }
Jika objek iterable diubah selama iterasi, maka hasil yang diperoleh iterator juga akan diubah.
konstanta arr = [1, 2, 3] konsol.log(arr) const iter = arr[Simbol.iterator]() konsol.log(iter.next()) arr[1] = 999 konsol.log(iter.next()) konsol.log(iter.next())
Ketika kita mengulangi ke done: true
, apakah kesalahan akan dilaporkan saat memanggil next
, atau tidak ada yang dikembalikan?
Namun tidak, iterator akan dalam keadaan selesai tetapi belum selesai . done: true
berarti sudah selesai, tetapi next
masih dapat dipanggil di masa depan, meskipun hasilnya akan selalu { value: undefined, done: true }
. Itu sebabnya dikatakan selesai tetapi tidak selesai .
konstanta arr = [1, 2, 3] const iter = arr[Simbol.iterator]() konsol.log(iter.next()) konsol.log(iter.next()) konsol.log(iter.next()) konsol.log(iter.next()) konsol.log(iter.next()) konsol.log(iter.next())
Dari contoh di atas, kita dapat mengetahui bahwa iterator dihasilkan melalui fungsi pabrik iterator Symbol.iterator
, jadi kita perlu mengimplementasikan fungsi pabrik iterator iterator, dan kemudian iterator dapat memanggil metode next
, jadi Anda juga perlu mengimplementasikan metode next
. Sedangkan untuk fungsi pabrik iterator, sebenarnya mengembalikan instance this
secara langsung.
Contoh pencacah:
kelas Penghitung { konstruktor(batas) { ini.hitungan = 1 this.limit = batas } Berikutnya() { if (ini.hitungan <= ini.batas) { kembali { selesai: salah, nilai: ini.hitung++ } } kalau tidak { kembali { selesai: benar, nilai: tidak ditentukan } } } [Simbol.iterator]() { kembalikan ini }}
penghitung konstan = Penghitung baru(3) const iter = counter[Simbol.iterator]() konsol.log(iter.next()) konsol.log(iter.next()) konsol.log(iter.next()) konsol.log(iter.next()) konsol.log(iter.next())
Sekilas tidak ada masalah, namun jika kita menggunakan for-of
untuk melakukan traverse, kita bisa menemukan masalahnya.
const counter = new Counter(3)for (biarkan i dari counter) { console.log(i)}console.log('Iterasi lain:')for (biarkan i penghitung) { konsol.log(i)}
Menggunakan loop for-of
juga membuatnya dapat dibuang. Ini karena count
adalah variabel contoh ini, jadi variabel yang sama digunakan di kedua iterasi. Namun, setelah loop pertama dari variabel tersebut, variabel tersebut telah melampaui batas, jadi Anda tidak akan mendapatkan apa pun dengan menggunakan loop for-of
. lagi. Hasilnya adalah.
Anda dapat memasukkan variabel count
ke dalam penutupan, dan kemudian mengembalikan iterator melalui penutupan, sehingga setiap iterator yang dibuat akan sesuai dengan penghitung baru.
kelas Penghitung { konstruktor(batas) { this.limit = batas } [Simbol.iterator]() { misalkan hitung = 1 const limit = this.limit pengembalian { // Fungsi pabrik iterator harus mengembalikan objek dengan metode selanjutnya, karena iterasi sebenarnya diimplementasikan dengan memanggil metode berikutnya next() { if (hitungan <= batas) { kembali { selesai: salah, nilai: hitungan++ } } kalau tidak { kembali { selesai: benar, nilai: tidak ditentukan } } } } }}
Uji
const counter = new Counter(3)for (biarkan i dari counter) { console.log(i)}console.log('Iterasi lain:')for (biarkan i penghitung) { konsol.log(i)}
sama seperti menggunakan perulangan for-of
. Iterator akan dengan cerdas memanggil metode next
. Ketika iterator berakhir lebih awal, ia juga akan memanggil metode return
.
[Simbol.iterator]() { misalkan hitung = 1 const limit = this.limit pengembalian { // Fungsi pabrik iterator harus mengembalikan objek dengan metode selanjutnya, karena iterasi sebenarnya diimplementasikan dengan memanggil metode selanjutnya next() { if (hitungan <= batas) { kembali { selesai: salah, nilai: hitungan++ } } kalau tidak { kembali { selesai: benar, nilai: tidak ditentukan } } }, kembali() { console.log('Penghentian iterator lebih awal') kembali { selesai: benar } } }}
Uji
const counter = new Counter(5)for (biarkan i dari counter) { jika (saya === 3) { merusak; } konsol.log(i)}
Jika iterator tidak ditutup, Anda dapat melanjutkan iterasi dari bagian terakhir yang Anda tinggalkan . Iterator array tidak dapat ditutup.
const arr = [1, 2, 3, 4, 5]const iter = arr[Simbol.iterator]()iter.return = function () { console.log('Keluar dari iterator lebih awal') kembali { selesai: benar }}untuk (const i dari iter) { konsol.log(i) jika (saya === 2) { merusak }}untuk (const i dari iter) { konsol.log(i)}