Menangani setiap item dalam koleksi adalah operasi yang sangat umum. JavaScript menyediakan banyak cara untuk mengulangi koleksi, mulai dari yang sederhana dan untuk setiap loop hingga peta (), filter () dan pemahaman array (derivasi array). Dalam JavaScript 1.7, iterator dan generator membawa mekanisme iteratif baru dalam sintaks JavaScript inti, dan juga menyediakan mekanisme untuk menyesuaikan perilaku untuk ... di dan untuk setiap loop.
Iterator
Iterator adalah objek yang mengakses elemen dalam urutan koleksi setiap kali dan melacak posisi iterasi saat ini dalam urutan itu. Dalam JavaScript Iterator adalah objek yang menyediakan metode berikutnya (), yang mengembalikan elemen berikutnya dalam urutan. Metode ini melempar pengecualian stopiterasi ketika semua elemen dalam urutan dilintasi.
Setelah objek Iterator dibuat, itu dapat disebut secara implisit dengan mengulangi secara eksplisit selanjutnya (), atau dengan menggunakan JavaScript untuk ... di dalam dan untuk setiap loop.
Iterator sederhana yang berulang di atas objek dan array dapat dibuat menggunakan iterator ():
Salinan kode adalah sebagai berikut:
var lang = {name: 'javascript', ulang tahunYear: 1995};
var it = iterator (lang);
Setelah inisialisasi selesai, metode berikutnya () dapat dipanggil untuk mengakses pasangan nilai kunci objek pada gilirannya:
Salinan kode adalah sebagai berikut:
var pair = it.next (); // Pasangan Nilai Kunci adalah ["Nama", "JavaScript"]
pair = it.next (); // pasangan nilai kunci adalah ["ulang tahun", 1995]
pair = it.next (); // Pengecualian `stopitation` dilemparkan
Untuk ... di loop dapat digunakan untuk mengganti panggilan eksplisit ke metode berikutnya (). Ketika pengecualian stopitasi dilemparkan, loop akan secara otomatis berakhir.
Salinan kode adalah sebagai berikut:
var it = iterator (lang);
untuk (pasangan var di dalamnya)
cetak (pasangan); // Satu [tombol, nilai] pasangan nilai kunci di dalamnya adalah output setiap kali
Jika Anda hanya ingin mengulangi nilai kunci objek, Anda dapat meneruskan parameter kedua ke fungsi Iterator (), dengan nilai yang benar:
Salinan kode adalah sebagai berikut:
var it = iterator (lang, true);
untuk (var kunci di dalamnya)
cetak (kunci); // Hanya nilai kunci output
Salah satu keuntungan menggunakan Iterator () untuk mengakses objek adalah bahwa properti khusus yang ditambahkan ke objek.Prototype tidak termasuk dalam objek urutan.
Iterator () juga dapat digunakan pada array:
Salinan kode adalah sebagai berikut:
var langs = ['javascript', 'python', 'haskell'];
var it = iterator (langs);
untuk (pasangan var di dalamnya)
cetak (pasangan); // Hanya output iterasi [indeks, bahasa] pasangan nilai kunci
Sama seperti melintasi suatu objek, hasil dari passing true ke traversal sebagai parameter kedua adalah indeks array:
Salinan kode adalah sebagai berikut:
var langs = ['javascript', 'python', 'haskell'];
var it = iterator (langs, true);
untuk (var i di dalamnya)
cetak (i); // output 0, lalu 1, lalu 2
Gunakan kata kunci LET untuk menetapkan indeks dan nilai untuk memblokir variabel secara terpisah di dalam loop, dan Anda juga dapat merusak penugasan (penugasan perusak):
Salinan kode adalah sebagai berikut:
var langs = ['javascript', 'python', 'haskell'];
var it = iterators (langs);
untuk (biarkan [i, lang] di dalamnya)
print (i + ':' + lang); // output "0: javascript" dll.
Menyatakan iterator khusus
Beberapa objek yang mewakili kumpulan elemen harus diulang dengan cara yang ditentukan.
1. Iterasi di atas objek yang mewakili kisaran (rentang) harus mengembalikan angka yang terkandung dalam kisaran ini satu per satu.
2. simpul daun pohon dapat diakses menggunakan kedalaman-pertama atau luas pertama
3. Iterasi atas objek yang mewakili hasil kueri basis data harus mengembalikan baris demi baris, bahkan jika seluruh set hasil belum dimuat ke dalam satu array tunggal.
4. Iterator yang bekerja pada urutan matematika yang tak terbatas (seperti urutan Fibonacci) harus mengembalikan hasil satu demi satu tanpa membuat struktur data panjang yang tak terbatas.
JavaScript memungkinkan Anda untuk menulis kode yang menyesuaikan logika iteratif dan menerapkannya pada suatu objek
Kami membuat objek rentang sederhana dengan dua nilai:
Salinan kode adalah sebagai berikut:
rentang fungsi (rendah, tinggi) {
this.low = rendah;
this.high = tinggi;
}
Sekarang kami membuat iterator khusus yang mengembalikan urutan semua bilangan bulat dalam jangkauan. Antarmuka Iterator mengharuskan kita untuk menyediakan metode berikutnya () untuk mengembalikan elemen berikutnya dalam urutan atau melemparkan pengecualian stopitasi.
Salinan kode adalah sebagai berikut:
Function Rangeriterator (rentang) {
this.range = range;
this.current = this.range.low;
}
Rangeiterator.prototype.next = function () {
if (this.current> this.range.high)
lempar stopitasi;
kalau tidak
kembalikan ini.current ++;
};
Rangeriterator kami dipakai oleh instance rentang sambil mempertahankan properti saat ini untuk melacak lokasi urutan saat ini.
Akhirnya, agar rangeriterator dikombinasikan dengan jangkauan, kita perlu menambahkan metode __iterator__ khusus untuk jangkauan. Ketika kami mencoba untuk mengulangi dalam jangkauan, itu akan dipanggil dan harus mengembalikan instance rangeriterator yang mengimplementasikan logika iteratif.
Salinan kode adalah sebagai berikut:
Range.prototype .__ iterator__ = function () {
mengembalikan rangeriterator baru (ini);
};
Setelah menyelesaikan iterator khusus kami, kami dapat mengulangi contoh lingkup:
Salinan kode adalah sebagai berikut:
var range = rentang baru (3, 5);
untuk (var i dalam jangkauan)
cetak (i); // output 3, lalu 4, lalu 5
Generator: Cara yang Lebih Baik untuk Membangun Iterator
Meskipun iterator khusus adalah alat yang berguna, Anda perlu merencanakannya dengan cermat saat membuatnya karena mereka perlu dipelihara secara eksplisit.
Generator menyediakan fungsi yang kuat: ini memungkinkan Anda untuk mendefinisikan fungsi yang berisi algoritma iteratif Anda sendiri, dan secara otomatis dapat mempertahankan keadaannya.
Generator adalah fungsi khusus yang dapat digunakan sebagai pabrik iterator. Jika suatu fungsi berisi satu atau lebih ekspresi hasil, itu disebut generator (catatan penerjemah: node.js juga perlu diwakili oleh * sebelum nama fungsi).
Catatan: Kata kunci hasil hanya dapat digunakan untuk blok kode dalam HTML yang termasuk dalam <skrip tipe = "Application/JavaScript; Versi = 1.7"> (atau lebih baru). Tag skrip XUL (XML User Interface Language) tidak memerlukan menentukan blok kode khusus ini untuk mengakses fitur -fitur ini.
Ketika fungsi generator dipanggil, badan fungsi tidak akan segera dieksekusi, ia akan mengembalikan objek generator-iterator. Setiap kali metode generator-generator berikutnya dipanggil, badan fungsi akan dieksekusi ke ekspresi hasil berikutnya dan kemudian mengembalikan hasilnya. Ketika fungsi mengakhiri atau menemukan pernyataan pengembalian, pengecualian stopiterasi akan dilemparkan.
Gunakan contoh untuk mengilustrasikan dengan lebih baik:
Salinan kode adalah sebagai berikut:
function SimpleGenerator () {
menghasilkan "pertama";
menghasilkan "kedua";
menghasilkan "ketiga";
untuk (var i = 0; i <3; i ++)
menghasilkan i;
}
var g = SimpleGenerator ();
print (g.next ()); // output "pertama"
print (g.next ()); // output "kedua"
print (g.next ()); // output "ketiga"
print (g.next ()); // output 0
print (g.next ()); // output 1
print (g.next ()); // output 2
print (g.next ()); // Lemparkan Pengecualian Penghentian
Fungsi generator dapat digunakan secara langsung oleh kelas sebagai metode __iterator__, dan dapat secara efektif mengurangi jumlah kode di mana iterator khusus diperlukan. Mari kita tulis ulang kisaran menggunakan generator:
Salinan kode adalah sebagai berikut:
rentang fungsi (rendah, tinggi) {
this.low = rendah;
this.high = tinggi;
}
Range.prototype .__ iterator__ = function () {
untuk (var i = this.low; i <= this.high; i ++)
menghasilkan i;
};
var range = rentang baru (3, 5);
untuk (var i dalam jangkauan)
cetak (i); // output 3, lalu 4, lalu 5
Tidak semua generator akan berakhir, Anda dapat membuat generator yang mewakili urutan tak terbatas. Generator berikut mengimplementasikan urutan fibonacci, di mana setiap elemen adalah jumlah dari dua yang pertama:
Salinan kode adalah sebagai berikut:
fungsi fibonacci () {
var fn1 = 1;
var fn2 = 1;
while (1) {
var arus = fn2;
fn2 = fn1;
fn1 = Fn1 + arus;
menghasilkan arus;
}
}
Var Sequence = Fibonacci ();
print (sequence.next ()); // 1
print (sequence.next ()); // 1
print (sequence.next ()); // 2
print (sequence.next ()); // 3
print (sequence.next ()); // 5
print (sequence.next ()); // 8
print (sequence.next ()); // 13
Fungsi generator dapat mengambil parameter dan akan menggunakan parameter ini ketika fungsi dipanggil untuk pertama kalinya. Generator dapat diakhiri (menyebabkannya melempar pengecualian stopitasi) dengan menggunakan pernyataan pengembalian. Varian fibonacci () berikut mengambil parameter batas opsional yang mengakhiri fungsi ketika kondisi dipicu.
Salinan kode adalah sebagai berikut:
fungsi fibonacci (limit) {
var fn1 = 1;
var fn2 = 1;
while (1) {
var arus = fn2;
fn2 = fn1;
fn1 = Fn1 + arus;
if (batas && saat ini> batas)
kembali;
menghasilkan arus;
}
}
Generator Fitur Tingkat Lanjut
Generator dapat menghitung nilai pengembalian hasil berdasarkan persyaratan, yang membuatnya mewakili persyaratan perhitungan urutan yang sebelumnya mahal, bahkan urutan tak terbatas yang ditunjukkan di atas.
Selain metode berikutnya (), objek generator-iterator juga memiliki metode Send (), yang dapat memodifikasi keadaan internal generator. Nilai yang diteruskan ke Send () akan diperlakukan sebagai hasil dari ekspresi hasil terakhir dan generator akan dijeda. Sebelum Anda memberikan nilai yang ditentukan menggunakan metode Send (), Anda harus menelepon selanjutnya () setidaknya sekali untuk memulai generator.
Generator Fibonacci berikut menggunakan metode Send () untuk memulai ulang urutan:
Salinan kode adalah sebagai berikut:
fungsi fibonacci () {
var fn1 = 1;
var fn2 = 1;
while (1) {
var arus = fn2;
fn2 = fn1;
fn1 = Fn1 + arus;
var reset = hasil arus;
if (reset) {
fn1 = 1;
fn2 = 1;
}
}
}
Var Sequence = Fibonacci ();
print (sequence.next ()); // 1
print (sequence.next ()); // 1
print (sequence.next ()); // 2
print (sequence.next ()); // 3
print (sequence.next ()); // 5
print (sequence.next ()); // 8
print (sequence.next ()); // 13
print (sequence.send (true))); // 1
print (sequence.next ()); // 1
print (sequence.next ()); // 2
print (sequence.next ()); // 3
CATATAN: Menariknya, Calling Send (tidak ditentukan) persis sama dengan Calling Next (). Namun, ketika metode Send () dipanggil untuk memulai generator baru, pengecualian TypeError akan dilemparkan kecuali tidak ditentukan.
Anda dapat memanggil metode lemparan dan melewati outlier yang harus dilemparkan untuk memaksa generator untuk melempar pengecualian. Pengecualian ini akan dilemparkan dari konteks saat ini dan menjeda generator, mirip dengan eksekusi hasil saat ini, tetapi diganti dengan pernyataan nilai lemparan.
Jika hasil tidak ditemui selama proses melempar pengecualian, pengecualian akan diteruskan sampai metode lempar () dipanggil, dan selanjutnya menelepon selanjutnya () akan menyebabkan pengecualian stopiterasi dilemparkan.
Generator memiliki metode tutup () untuk memaksa generator berakhir. Mengakhiri generator akan memiliki efek berikut:
1. Semua kalimat akhirnya valid di generator akan dieksekusi
2. Jika kata akhirnya melempar pengecualian kecuali stopitasi, pengecualian akan diteruskan ke pemanggil metode tutup ().
3. Generator akan berakhir
Ekspresi Generator
Salah satu kelemahan yang jelas dari derivasi array adalah bahwa mereka menyebabkan seluruh array dibangun dalam memori. Overhead inputing ke derivasi tidak signifikan ketika overheadnya adalah susunan kecil itu sendiri - namun, masalah dapat muncul ketika array input besar atau saat membuat generator array yang mahal (atau tak terbatas) baru.
Generator memungkinkan komputasi malas untuk menghitung elemen sesuai kebutuhan saat diperlukan. Ekspresi generator secara sintaksis hampir sama dengan derivasi array - menggunakan tanda kurung alih -alih tanda kurung persegi (dan digunakan untuk ... bukan untuk masing -masing ... di) - tetapi itu menciptakan generator alih -alih array sehingga perhitungan dapat ditunda. Anda dapat menganggapnya sebagai sintaksis singkat untuk membuat generator.
Misalkan kita memiliki iterator untuk mengulangi serangkaian bilangan bulat yang sangat besar. Kita perlu membuat iterator baru untuk mengulangi angka genap. Derivasi array akan membuat seluruh array yang berisi semua angka genap dalam memori:
Salinan kode adalah sebagai berikut:
var ganda = [i * 2 untuk (i di dalamnya)];
Ekspresi generator akan membuat iterator baru dan menghitung nilai genap sesuai kebutuhan saat diperlukan:
Salinan kode adalah sebagai berikut:
var it2 = (i * 2 untuk (i di dalamnya));
cetak (it2.next ()); // angka genap pertama di dalamnya
cetak (it2.next ()); // angka genap kedua di dalamnya
Ketika generator digunakan sebagai parameter fungsi, tanda kurung digunakan sebagai panggilan fungsi, yang berarti bahwa tanda kurung terluar dapat dihilangkan:
Salinan kode adalah sebagai berikut:
var result = dosomething (i * 2 untuk (i di dalamnya));
Akhir.