Penutupan ialah ciri penting dalam JavaScript Peranan terbesarnya ialah menyimpan maklumat semasa menjalankan fungsi. Dalam JavaScript, banyak ciri penutupan berasal daripada rantai skop semasa panggilan fungsi.
Rantai skop objek panggilan fungsi dan pembolehubah
Untuk setiap panggilan fungsi dalam JavaScript, JavaScript akan mencipta objek setempat untuk menyimpan pembolehubah setempat yang ditakrifkan dalam fungsi jika terdapat fungsi bersarang yang ditakrifkan di dalam fungsi, maka JavaScript akan Menentukan objek tempatan bersarang di atas objek yang sudah ada objek tempatan yang ditentukan. Untuk fungsi, terdapat seberapa banyak lapisan objek tempatan bersarang kerana terdapat lapisan definisi fungsi bersarang di dalamnya. Objek tempatan ini dipanggil "objek panggilan fungsi" ("objek panggilan" dalam ECMAScript 3, dinamakan semula sebagai "rekod persekitaran deklaratif" dalam ECMAScript 5, tetapi secara peribadi saya fikir nama dalam ECMAScript 3 lebih mudah difahami). Ambil panggilan fungsi berikut sebagai contoh:
Dalam contoh mudah ini, apabila fungsi f() dipanggil, JavaScript akan mencipta objek panggilan bagi fungsi f() (mari kita panggil ia f_invokeObj Terdapat dua atribut di dalam objek f_invokeObj: a dan x; menjalankan f(), nilai a ialah 10 dan nilai x ialah 6, jadi hasil pulangan akhir ialah 60. Ikonnya adalah seperti berikut:
Apabila fungsi bersarang wujud, JavaScript akan mencipta berbilang objek panggilan fungsi:
Dalam contoh ini, apabila fungsi f() dipanggil, JavaScript akan mencipta objek panggilan (f_invokeObj) bagi fungsi f(), yang mempunyai dua atribut dalaman a dan x Nilai a ialah 10 dan nilai x ialah 6; jalankan f (), JavaScript akan menghuraikan dan mentakrifkan fungsi g() dalam fungsi f(), dan mencipta objek panggilan (g_invokeObj) bagi g(), yang mempunyai atribut dalaman b, dan nilai b ialah sama dengan parameter masuk x, iaitu 6 , jadi hasil pulangan akhir ialah 360. Ikonnya adalah seperti berikut:
Seperti yang anda lihat, objek panggilan fungsi membentuk rantai. Apabila fungsi terbenam g() sedang berjalan dan perlu mendapatkan nilai pembolehubah, carian akan bermula dari objek panggilan fungsi yang terdekat Jika ia tidak boleh dicari, ia akan mencari dalam objek panggilan lanjut di sepanjang rantaian objek panggilan fungsi apa yang dipanggil "rantai skop pembolehubah". Jika pembolehubah yang sama muncul dalam dua objek panggilan fungsi, fungsi akan mengambil nilai pembolehubah dalam objek panggilan yang paling hampir dengan dirinya:
Dalam contoh di atas, pembolehubah a wujud dalam kedua-dua objek panggilan bagi fungsi g() (g_invokeObj) dan objek panggilan bagi fungsi f() (f_invokeObj) dan nilai a adalah berbeza Apabila fungsi g() adalah run, dalam g() Nilai yang digunakan di dalam fungsi ialah 1, manakala nilai yang digunakan di luar fungsi g() ialah 10. Rajah menunjukkan rantai objek panggilan fungsi pada masa ini seperti berikut:
Apakah itu penutupan?
Semua fungsi (fungsi) dalam JavaScript adalah objek, dan apabila fungsi ditakrifkan, rantai objek panggilan fungsi yang sepadan akan dijana Definisi fungsi sepadan dengan rantai objek panggilan fungsi. Selagi objek fungsi wujud, objek panggilan fungsi yang sepadan wujud apabila fungsi tidak lagi digunakan, objek panggilan fungsi yang sepadan akan dikumpul dan gabungan satu-satu antara objek fungsi dan objek panggilan fungsi rantai, Hanya panggil ia "penutupan". Dalam contoh fungsi f() dan fungsi g() di atas, terdapat dua penutupan: objek fungsi f() dan objek f_invokeObj membentuk penutupan, dan objek fungsi g() dan rantai objek g_invokeObj-f_invokeObj membentuk bersama-sama Penutupan kedua. Apabila fungsi g() selesai melaksanakan, penutupan g() dikumpul kerana fungsi g() tidak lagi digunakan kemudian, apabila fungsi f() selesai dilaksanakan, f() ditutup atas sebab yang sama bungkusan itu juga sampah dikutip.
Daripada definisi penutupan, kita boleh membuat kesimpulan bahawa semua fungsi JavaScript adalah penutupan selepas definisi - kerana semua fungsi adalah objek, semua fungsi juga mempunyai rantai objek panggilan yang sepadan selepas pelaksanaan.
Walau bagaimanapun, di mana penutupan benar-benar berlaku adalah dalam kes fungsi bersarang. Oleh kerana fungsi sebaris ditakrifkan apabila fungsi luaran sedang berjalan, nilai pembolehubah yang disimpan dalam penutupan fungsi sebaris (terutamanya nilai pembolehubah tempatan fungsi luaran) adalah nilai semasa larian ini. Selagi objek fungsi tertanam masih wujud, penutupannya masih wujud (nilai pembolehubah dalam penutupan tidak akan berubah), sekali gus mencapai tujuan menyimpan maklumat tentang proses berjalan fungsi. Pertimbangkan contoh ini:
Dalam contoh ini, apabila fungsi f() dijalankan, fungsi g() ditakrifkan dan penutupan fungsi g() dibuat Penutupan g() mengandungi rantai objek g_invokeObj-f_invokeObj, sekali gus menjimatkan f() Nilai pembolehubah a semasa pelaksanaan fungsi. Apabila pernyataan console.log() dilaksanakan, kerana objek fungsi g masih wujud, penutupan g() masih wujud apabila menjalankan objek fungsi g yang masih wujud, JavaScript akan menggunakan penutupan g() yang masih wujud dan Dapatkan nilai pembolehubah a ("di dalam") daripadanya.