Artikel ini memberi Anda pengetahuan yang relevan tentang JavaScript, terutama memperkenalkan iterator front-end JavaScript dan Generator. Teman-teman yang membutuhkan dapat merujuknya.
Entri front-end (vue) ke kursus penguasaan: masuk ke pembelajaran
Iterator menyediakan mekanisme antarmuka terpadu untuk menyediakan mekanisme akses terpadu untuk berbagai struktur data yang berbeda.
Mendefinisikan Iterator adalah menyediakan objek dengan metode next(). Setiap kali next() dipanggil, objek hasil akan dikembalikan. Objek hasil memiliki dua atribut, nilai mewakili nilai saat ini, dan selesai mewakili apakah traversal selesai .
fungsi makeIterator(Array){ biarkan indeks = 0; kembali { selanjutnya: fungsi(){ kembali ( Array.panjang> indeks? {nilai: Array[indeks++]}: {selesai: benar} ) } } } biarkan iterator = makeIterator(['1','2']) konsol.log(iterator.next()); // {nilai: '1'} konsol.log(iterator.next()); // {nilai: '2'} konsol.log(iterator.next()); // {selesai: benar}
Peran Iterator:
Menyediakan antarmuka akses terpadu dan sederhana untuk berbagai struktur data;
Memungkinkan anggota struktur data disusun dalam urutan tertentu;
untuk dikonsumsi oleh untuk...dari
ES6 menyediakan pernyataan for of untuk melintasi objek iterator. Kita akan menggunakan pernyataan for of untuk melintasi iterator yang dibuat di atas:
biarkan iterator = makeIterator(['1','2']) for (biarkan nilai iterator) { console.log(nilai); } // iterator tidak dapat diulang
Hasilnya adalah kesalahan yang mengatakan iterator tidak dapat diubah. Mengapa demikian? ES6 menetapkan bahwa antarmuka Iterator default diterapkan di properti Symbol.iterator pada struktur data. Jika struktur data memiliki properti Symbol.iterator, struktur data dapat dilintasi.
Kami mengubah makeIterator khusus sebagai berikut:
const MakeIterator = (Array) => ({ [Simbol.iterator](){ biarkan indeks = 0; kembali { Berikutnya(){ biarkan panjang = Array.panjang; if(indeks < panjang){ kembalikan {nilai: Array[index++]} }kalau tidak{ kembali {selesai: benar} } } } } }) for(biarkan nilai MakeIterator([1,2])){ konsol.log(nilai) } // 1 // 2
Kita menambahkan metode return ke MakeIterator. Jika perulangan for...of keluar lebih awal (biasanya karena kesalahan atau pernyataan break), metode return() akan dipanggil untuk menghentikan traversal.
Berdasarkan fitur ini, jika suatu objek perlu membersihkan atau melepaskan sumber daya sebelum menyelesaikan traversal, kita dapat menerapkan metode return() dan menyertakan penutupan file ketika pembacaan file gagal.
const MakeIterator = (Array) => ({ [Simbol.iterator](){ biarkan indeks = 0; kembali { Berikutnya(){ biarkan panjang = Array.panjang; if(indeks < panjang){ kembalikan {nilai: Array[index++]} }kalau tidak{ kembali {selesai: benar} } }, kembali(){ kembali {selesai: benar} } } } }) for(biarkan nilai MakeIterator([1, 2, 3])){ konsol.log(nilai) // 1 // Metode 1 merusak; // Metode 2 // melempar Kesalahan baru('kesalahan'); }
susunan
Mengatur
Peta
Objek mirip array, seperti objek argumen, objek DOM NodeList, objek typedArray
// argumen objek fungsi jumlah(){ for(biarkan nilai argumen){ konsol.log(nilai) } } jumlah(1,2) // 1 // 2 // objek typedArray biarkan typeArry = new Int8Array(2); ketikArry[0] = 1; ketikArry[1] = 2; for(biarkan nilai typeArry){ konsol.log(nilai) } // 1 // 2
Objek pembangkit
fungsi*gen(){ hasil 1; hasil 2; } for(biarkan nilai gen()){ konsol.log(nilai) }
Rangkaian
T: Mengapa Object tidak memiliki Iterator asli?
J: Alasan mengapa Object tidak menggunakan antarmuka Iterator secara default adalah karena tidak pasti properti objek mana yang dilintasi terlebih dahulu dan properti mana yang dilintasi kemudian.
Intinya, traverser adalah proses linier. Untuk struktur data non-linier apa pun, penerapan antarmuka traverser setara dengan penerapan transformasi linier.
Namun, sebenarnya, antarmuka traverser penerapan objek tidak diperlukan, karena saat ini objek yang sebenarnya digunakan sebagai struktur Peta tidak memiliki struktur Peta, tetapi ES6 menyediakannya secara asli.
Menghancurkan tugas
biarkan set = new Set().add('a').add('b').add('c'); misalkan [x,y] = himpunan; // x='a';
operator penyebaran
var str = 'halo'; [...str] // ['h','e','l','l','o']
Operator spread memanggil antarmuka Iterator, jadi Object tidak menyebarkan antarmuka Iterator, jadi mengapa... operator dapat digunakan?
Alasan: Ada dua jenis operator spread
Satu digunakan dalam kasus parameter fungsi dan perluasan array. Dalam hal ini, objek harus dapat diubah (iterable)
Yang lainnya untuk perluasan objek, yaitu bentuk {...obj}. Dalam hal ini, objek harus dapat dihitung (enumerable)
misalkan obj1 = { nama: 'qianxun' } misalkan obj2 = { usia: 3 } // Objek array dapat dihitung let obj = {...obj1, ...obj2} console.log(obj) //{nama: 'qianxun', umur: 3} // Objek biasa tidak dapat diubah secara default let obj = [...obj1, ...obj2] console.log(obj) // objek tidak dapat diubah
fungsi forOf(obj, cb){ biarkan iteratorValue = obj[Simbol.iterator](); biarkan hasil = iteratorValue.next() while(!hasil.selesai){ cb(hasil.nilai) hasil = iteratorValue.next() } } forOf([1,2,3], (nilai)=>{ konsol.log(nilai) }) // 1 // 2 // 3
Secara konseptual
Fungsi generator adalah solusi pemrograman asinkron yang disediakan oleh ES6. Fungsi Generator adalah mesin status yang merangkum beberapa status internal;
Fungsi Generator juga merupakan fungsi pembuatan objek traverser, yang mengembalikan objek traverser setelah eksekusi.
resmi
1. Terdapat tanda bintang di antara kata kunci fungsi dan nama fungsi;
2. Ekspresi hasil digunakan di dalam badan fungsi untuk menentukan status internal yang berbeda.
fungsi* simpleGenerator(){ hasil 1; hasil 2; } generator sederhana()
Seperti di atas, kami membuat Generator sederhana, dan kami menjelajahinya dengan dua pertanyaan:
Apa yang terjadi setelah fungsi Generator dijalankan?
Apa fungsi ekspresi hasil dalam fungsi?
fungsi* simpleGenerator(){ console.log('halo dunia'); hasil 1; hasil 2; } biarkan generator = simpleGenerator(); // simpleGenerator {<ditangguhkan}} konsol.log(generator.next()) // halo dunia // {nilai: 1, selesai: salah} konsol.log(generator.next()) // {nilai: 2, selesai: salah}
Fungsi generator generator mengembalikan objek generator setelah dijalankan, sedangkan fungsi biasa akan langsung mengeksekusi kode di dalam fungsi tersebut, setiap kali metode berikutnya dari objek generator dipanggil, fungsi tersebut akan dieksekusi hingga kata kunci hasil berikutnya menghentikan eksekusi, dan objek {nilai: Nilai, selesai: Boolean}.
Ekspresi hasil itu sendiri tidak memiliki nilai kembalian, atau selalu mengembalikan nilai yang tidak ditentukan. Metode selanjutnya dapat mengambil satu parameter, yang akan diperlakukan sebagai nilai kembalian ekspresi hasil sebelumnya. Melalui parameter metode selanjutnya, nilai yang berbeda dapat disuntikkan dari luar ke dalam pada tahapan fungsi Generator yang berbeda untuk menyesuaikan perilaku fungsi. Karena parameter metode berikutnya mewakili nilai kembalian ekspresi hasil sebelumnya, parameter yang diteruskan tidak valid saat pertama kali metode berikutnya digunakan.
jumlah fungsi(x){ fungsi pengembalian(y){ kembalikan x + y; } } konsol.log(jumlah(1)(2)) // Gunakan parameter selanjutnya untuk menulis ulang fungsi* sum(x){ misalkan y = hasil x; sementara(benar){ y = hasil x + y; } } misalkan gen = jumlah(2) konsol.log(gen.next()) // 2 konsol.log(gen.next(1)) // 3 konsol.log(gen.next(2)) // 4
Peran ekspresi hasil: mendefinisikan keadaan internal dan menjeda eksekusi. Perbedaan antara ekspresi hasil dan pernyataan pengembalian
Ekspresi hasil berarti bahwa fungsi menghentikan sementara eksekusi dan terus mengeksekusi mundur dari posisi tersebut di waktu berikutnya, sedangkan pernyataan return tidak memiliki fungsi memori posisi.
Dalam suatu fungsi, hanya satu pernyataan return yang dapat dieksekusi, namun beberapa ekspresi hasil dapat dieksekusi.
Fungsi apa pun dapat menggunakan pernyataan return. Ekspresi hasil hanya dapat digunakan dalam fungsi Generator. Jika digunakan di tempat lain, kesalahan akan dilaporkan.
Jika ekspresi hasil berpartisipasi dalam operasi, masukkan ke dalam tanda kurung; jika digunakan sebagai parameter fungsi atau ditempatkan di sisi kanan ekspresi penugasan, tanda kurung tidak boleh disertakan.
fungsi *gen() { console.log('halo' + hasil) × console.log('halo' + (hasil)) √ console.log('halo' + hasil 1) × console.log('halo' + (hasil 1)) √ foo(hasil 1) √ const param = hasil 2 √ }
Berdasarkan fakta bahwa fungsi Generator generator dapat mendukung banyak hasil, kita dapat menerapkan skenario di mana suatu fungsi memiliki beberapa nilai kembalian:
fungsi* gen(angka1, angka2){ hasil angka1 + angka2; hasil nomor1 - nomor2; } misalkan res = gen(2, 1); console.log(res.next()) // {nilai: 3, selesai: false} console.log(res.next()) // {nilai: 1, selesai: false}
Karena fungsi Generator adalah fungsi pembangkitan iterator, Generator dapat ditugaskan ke properti Symbol.iterator objek, sehingga objek tersebut memiliki antarmuka Iterator. Kode implementasi generator lebih ringkas.
misalkan obj = { nama: 'qianxun', usia: 3, [Simbol.iterator]: function(){ biarlah itu = ini; biarkan kunci = Objek.kunci(itu) biarkan indeks = 0; kembali { berikutnya: fungsi(){ kembalikan indeks <kunci.panjang? {nilai: itu[kunci[index++]], selesai: salah}: {nilai: tidak ditentukan, selesai: benar} } } } } for(biarkan nilai objek){ konsol.log(nilai) }
Generator:
misalkan obj = { nama: 'qianxun', usia: 3, [Simbol.iterator]: fungsi* (){ biarkan kunci = Objek.kunci(ini) for(biarkan i=0; i< kunci.panjang; i++){ hasilkan ini[kunci[i]]; } } } for(biarkan nilai objek){ konsol.log(nilai) }
Metode
return()
dapat mengembalikan nilai yang diberikan dan menghentikan fungsi Generator traversal.
fungsi*gen() { hasil 1; hasil 2; hasil 3; } var g = gen(); g.next() // { nilai: 1, selesai: salah } // Jika tidak ada parameter yang diberikan saat metode return() dipanggil, atribut value dari nilai yang dikembalikan tidak ditentukan g.return('foo') // { nilai: "foo", selesai: benar } g.next() // { nilai: tidak terdefinisi, selesai: benar }
Jika ada blok kode try...finally
di dalam fungsi Generator dan blok kode try
sedang dieksekusi, maka metode return()
akan menyebabkan blok kode finally
dimasukkan segera. Setelah eksekusi, seluruh fungsi akan berakhir.
fungsi* angka () { hasil 1; mencoba { hasil 2; hasil 3; } Akhirnya { hasil 4; hasil 5; } hasil 6; } var g = angka(); g.next() // { nilai: 1, selesai: salah } g.selanjutnya() // { nilai: 2, selesai: salah } g.return(7) // { nilai: 4, selesai: salah } g.selanjutnya() // { nilai: 5, selesai: salah } g.next() // { nilai: 7, selesai: benar }
Jika Anda ingin memanggil fungsi Generator lain di dalam fungsi Generator. Kita perlu menyelesaikan traversal secara manual dalam badan fungsi sebelumnya. Jika pemanggilan fungsi disarangkan pada beberapa level, penulisannya akan rumit dan sulit dibaca.
Delegasikan ke generator lain
fungsi* g1() { hasil 2; hasil 3; } fungsi* g2() { hasil 1; hasil* g1(); hasil 4; } const iterator = g2(); console.log(iterator.next()); // { nilai: 1, selesai: salah } console.log(iterator.next()); // { nilai: 2, selesai: false } console.log(iterator.next()); // { nilai: 3, selesai: false } console.log(iterator.next()); // { nilai: 4, selesai: false } console.log(iterator.next()); // { nilai: tidak ditentukan, selesai: benar }
Delegasikan ke objek iterable lainnya
fungsi*gen(){ hasil* [1,2,3] } console.log(gen().next()) // {nilai: 1, selesai: false}
Fungsi Generator mengembalikan traverser. ES6 menetapkan bahwa traverser ini adalah turunan dari fungsi Generator dan mewarisi metode pada objek Generator.prototype, tetapi tidak dapat memperoleh properti ini karena ini adalah objek global saat ini, bukan instance. obyek.
fungsi*gen(){ ini.a = 1 } gen.prototype.say = fungsi(){ konsol.log('hai') } biarkan obj = gen() console.log(obj instanceof gen) // benar obj.say() // hai obj.berikutnya() console.log(obj.a) //tidak terdefinisi
Jika Anda ingin mengakses properti instance seperti konstruktor, Anda dapat memodifikasinya untuk mengikatnya ke Generator.prototype.
fungsi*gen(){ ini.a = 1 } gen.prototype.say = fungsi(){ konsol.log('hai') } biarkan obj = gen.panggilan(gen.prototipe) console.log(obj instanceof gen) // benar obj.say() // hai obj.berikutnya() konsol.log(obj.a) //1
fungsi* StateMachine(negara bagian){ biarkan transisi; sementara(benar){ if(transisi === "PENINGKATAN"){ negara++; }else if(transisi === "PENURUNAN"){ negara--; } transisi = keadaan hasil; } } const iterator = Mesin Negara(0); konsol.log(iterator.next()); // 0 console.log(iterator.next('PENINGKATAN')); // 1 konsol.log(iterator.next('DECREMENT')); // 0