Dalam proses pembelajaran front-end, kita pasti akan menemui banyak masalah, jadi hari ini kita akan membahas dua pertanyaan dari sudut pandang pemula:
Apa itu penutupan?
Apa fungsi penutupan?
Faktanya, penutupan ada dimana-mana saat kita mempelajari JavaScript, Anda hanya perlu bisa mengenali dan menerimanya. Penutupan bukanlah alat yang memerlukan pembelajaran sintaksis atau pola baru untuk digunakan. Penutupan adalah konsekuensi alami dari penulisan kode berdasarkan cakupan leksikal. Kita jarang perlu membuat penutupan dengan sengaja saat menulis kode.
Saya yakin saat ini banyak teman yang bergumam di dalam hati, apa itu ruang lingkup leksikal? Jangan panik, dengarkan saja saya pelan-pelan. Singkatnya, ruang lingkup leksikal adalah ruang lingkup yang didefinisikan dalam tahap leksikal. Dengan kata lain, cakupan leksikal ditentukan oleh tempat Anda menempatkan variabel dan cakupan tingkat blok saat Anda menulis kode, sehingga cakupannya tetap tidak berubah saat penganalisis leksikal memproses kode (sebagian besar waktu). ——"JavaScript Anda Tidak Tahu"
Mari kita ambil contoh dulu:
function test(){ var arr = [] untuk(var i=0;i<10;i++){ arr[i]=fungsi(){ konsol.log(i); } } kembali arr } var myArr = tes() // Arrku[0]() // Arrku[1]() // ... untuk(var j = 0; j < 10; j++){ Arrku[j]() } //Untuk menghindari kebosanan, loop kedua digunakan di sini untuk memanggil fungsi pada loop pertama dalam fungsi pengujian dan mencetak sepuluh hasil.
Mari kita analisis kode ini terlebih dahulu: Ketika kode ini dijalankan, menurut akal sehat, itu harus dianalisis Ini mencetak sepuluh angka dari 0 hingga 9 secara berurutan; tetapi loop for tidak memerlukan waktu untuk dijalankan (diabaikan dalam mikrodetik). );}, fungsi dalam array tidak dijalankan saat ini Ketika var myArr = test() memanggil fungsi pengujian, karena waktu eksekusi perulangan for diabaikan, i sudah menjadi 10 saat ini, jadi berapa? yang dicetak adalah 10 dari 10.
Saya yakin seseorang akan bertanya saat ini, apa hubungannya dengan penutupan yang akan kita bicarakan? Jadi jika kita sedikit memodifikasi kode ini dan mengubahnya menjadi akumulator, bagaimana kita bisa mengimplementasikannya?
Saya yakin akan ada orang-orang besar saat ini yang akan berkata, bukankah itu sederhana?
Ubah definisi var menjadi definisi let sehingga perulangan for pertama menjadi cakupan tingkat blok, kemudian dapat menjadi akumulator. Tentu tidak ada masalah,
tapi yang kita bicarakan hari ini adalah bagaimana mengimplementasikan akumulator di ES5. Lalu mari kita lihat kode berikut:
function test(){ var arr = [] untuk(var i=0;i<10;i++){ (fungsi(j){ arr[j]=fungsi(){ konsol.log(j); } })(Saya) } kembali arr } var myArr = tes() untuk(var j = 0; j < 10; j++){ Arrku[j]() }
Teman yang berhati-hati pasti akan menemukan bahwa ini adalah mengubah isi fungsi dalam loop menjadi fungsi yang dijalankan sendiri, tetapi hasil keluaran kali ini adalah keluaran sepuluh angka dari 0 hingga 9 secara berurutan, dan ini termasuk Paket penutupan, ketika kita mulai mengeksekusi kode ini, loop for kedua akan dipanggil sepuluh kali. Ketika setiap fungsi yang dijalankan sendiri dijalankan, objek AO dari fungsi yang dijalankan sendiri akan dibuat .Nama atributnya adalah j. Biasanya, setelah fungsi eksekusi dijalankan, objek AO-nya harus dimusnahkan. Namun, ketika myarr[j] () dijalankan, objek AO dari arr[j] berada di bagian atas rantai cakupan. sekarang saya mencari nama atribut j, tetapi saya tidak menemukannya. Saya mencari rantai cakupan dan menemukannya di objek AO dari fungsi yang dijalankan sendiri. Oleh karena itu, ketika fungsi yang dijalankan sendiri berakhir, AO-nya objek tidak akan didaur ulang oleh mekanisme pengumpulan sampah, jika tidak, kesalahan akan dilaporkan ketika myarr[j] () dijalankan, dan penutupan akan dilakukan.
Mari kita ambil contoh lain
fungsi a(){. fungsi b(){ var bbb = 234 konsol.log(aaa); } varaaa = 123 return b // b lahir di a, tapi diselamatkan} varglob = 100 var demo = a()Pertama-tama kita menggunakan pra-kompilasi untuk menganalisis kode
demo()
. Pertama, tentukan objek GO global. Cari deklarasi global dan temukan deklarasi variabel global. Deklarasi variabel akan digunakan sebagai nama atribut GO tidak terdefinisi. Temukan di deklarasi global. Untuk deklarasi fungsi, nama fungsi digunakan sebagai nama atribut objek GO, dan nilainya ditetapkan ke badan fungsi. Saat ini seharusnya GO{ glob: undefinisi--->100; demo: tidak terdefinisi; a: fa(){} }; Kemudian buat AO{ aaa: undefinisi--->123;b: fb() untuk fungsi a {} }, dan terakhir kompilasi fungsi b dalam fungsi a untuk membuat AO dari b { b: tidak terdefinisi--->234} saat ini, urutan rantai cakupan adalah 1. objek AO dari fungsi b; 2. Objek AO fungsi a; 3. Objek GO global. Saat kita mencetak aaa di fungsi b, kita mulai dari atas rantai cakupan. Jika tidak ada aaa di objek AO fungsi b, kita akan mencari ke bawah sepanjang rantai cakupan untuk menemukan AO dari fungsi tingkat kedua a. . Tujuannya adalah untuk menemukan nilai aaa sebagai 123 dan menampilkan hasilnya.
Jika kami tidak menganalisisnya dari sudut pandang prakompilasi, kami akan berpikir bahwa aaa seharusnya melaporkan kesalahan saat ini. Ketika var demo = a() dijalankan, ketika eksekusi fungsi a berakhir, maka objek AO yang sesuai a harus dimusnahkan. Menurut analisis akal sehat: Saat kita menjalankan demo, rantai cakupan harus membuat objek AO dan objek GO dari b. Saat ini, hanya ada objek AO dari b dan tidak ada objek AO dari a. Nilai aaa sebaiknya tidak dicetak, namun saat ini nilai aaa Nilainya 123 yang artinya objek AO a belum dimusnahkan, lalu kenapa? Alasannya adalah bahwa penutupan dibuat di sini. Ketika eksekusi var demo = a() selesai, mekanisme pengumpulan sampah akan bertanya, Saudaraku suatu fungsi, saya pikir Anda telah selesai menjalankannya ? , tetapi saat ini fungsi a hanya bisa menggelengkan kepalanya tanpa daya dan berkata, Saudaraku, saya tidak yakin apakah saya telah menyelesaikan eksekusinya. Setelah saya menjalankannya, saya membuat a b, tetapi b tidak di bawah kendali saya, jadi saya tidak yakin apakah b. dipanggil, jadi saya tidak yakin apakah saya telah menyelesaikan eksekusi. Mekanisme pengumpulan sampah memikirkannya. Saya harus melaporkan kesalahan, jadi saat ini objek AO tidak didaur ulang.
Saya yakin melalui dua contoh ini, Anda sudah memiliki pemahaman umum tentang penutupan. Selanjutnya, mari kita bahas tentang fungsi penutupan.
Fungsipenutupan
adalah
- dapat
- untuk mengimplementasikan variabel publik. Misalnya: akumulator (3.js)
- di-cache
- untuk mencapai enkapsulasi, privatisasi,
- dan pengembangan atribut modular untuk mencegah kontaminasi variabel global.
3.js).
jumlah var = 0 fungsi tambah() { jumlah pengembalian++ } konsol.log(tambahkan()); konsol.log(tambahkan()); console.log(add());
Ini adalah kode akumulasi yang relatif umum, tetapi jika selama magang atau bahkan di tempat kerja, perusahaan mengharuskan Anda untuk merangkum akumulator ke dalam kode modular, maka saat ini, demi kepentingan module Kita sebisa mungkin menghindari pendefinisian variabel global, tapi bagaimana kita bisa mencapainya tanpa mendefinisikan variabel global? Saat ini kita bisa menggunakan penutupan;
fungsi tambah() { jumlah var = 0 fungsi a() { ++hitungan console.log(hitungan); } kembali a } var res = tambahkan() res() res() //Setelah fungsi add berakhir, objek AO add tidak dimusnahkan, karena setelah fungsi add dijalankan, a yang dikembalikan membentuk penutupan tanpa mengetahui apakah sudah dipanggil, sehingga dapat dienkapsulasi ke dalam modul tanpa menggunakan variabel global.
Jadi ini adalah beberapa pendapat pribadi saya tentang penutupan dan fungsinya. Saat ini, saya hanya memiliki pemahaman yang dangkal tentang penutupan. Setelah dipelajari dan diperbaiki, akan ada artikel berikutnya tentang penutupan benar, dan membuat kemajuan bersama.