Penutupan adalah fitur penting dalam JavaScript, dan fungsi terbesar mereka adalah menyimpan informasi selama operasi fungsi. Dalam JavaScript, banyak fitur penutupan berasal dari rantai lingkup selama panggilan fungsi.
Rantai lingkup fungsi objek panggilan dan variabel
Untuk setiap panggilan fungsi dalam JavaScript, JavaScript akan membuat objek lokal untuk menyimpan variabel lokal yang ditentukan dalam fungsi; Jika ada fungsi bersarang di dalam fungsi, JavaScript akan mendefinisikan objek lokal bersarang pada objek lokal yang sudah ditentukan. Untuk suatu fungsi, ada banyak lapisan definisi fungsi bersarang seperti yang ada, sebanyak lapisan objek lokal bersarang seperti yang ada. Objek lokal ini disebut "Objek Panggilan Fungsi" ("Call Object" dalam ECMASCRIPT 3, dan diganti namanya menjadi "Catatan Lingkungan Deklaratif" dalam ECMASCRIPT 5, tetapi saya pribadi berpikir bahwa nama dalam ECMASCRIPS 3 lebih mudah dipahami). Panggilan fungsi berikut digunakan sebagai contoh:
Salinan kode adalah sebagai berikut:
fungsi f (x) {
var a = 10;
mengembalikan A*x;
}
console.log (f (6)); // 60
Dalam contoh sederhana ini, ketika fungsi f () dipanggil, JavaScript akan membuat objek panggilan fungsi f () (sebut saja f_invokeobj). Ada dua properti di dalam objek f_invokeobj: A dan X; Ketika f () dijalankan, nilai A 10 dan nilai x adalah 6, jadi hasil pengembalian akhir adalah 60. Ilustrasi adalah sebagai berikut:
Ketika ada fungsi Nesting, JavaScript akan membuat beberapa objek panggilan fungsi:
Salinan kode adalah sebagai berikut:
fungsi f (x) {
var a = 10;
mengembalikan a*g (x);
fungsi g (b) {
mengembalikan b*b;
}
}
console.log (f (6)); // 360
Dalam contoh ini, saat memanggil fungsi f (), JavaScript akan membuat objek panggilan fungsi f () (f_invokeObj), yang memiliki dua atribut A dan X, dan nilai A adalah 10 dan nilai x adalah 6; Saat menjalankan f (), JavaScript akan mengurai dan menentukan fungsi g () dalam fungsi f () dan membuat objek panggilan g () (g_invokeobj), yang memiliki atribut B, dan nilai B sama dengan parameter yang diteruskan x, sehingga hasil pengembalian akhir adalah 360. Ilustrasinya adalah sebagai berikut:
Seperti yang Anda lihat, objek panggilan fungsi membentuk rantai. Ketika fungsi tertanam g () sedang berjalan dan perlu mendapatkan nilai variabel, ia akan mulai mencari dari objek panggilan fungsi terbaru. Jika tidak dapat dicari, mencari di objek panggilan lebih lanjut di sepanjang fungsi rantai objek panggilan, yang disebut "rantai ruang lingkup variabel". Jika variabel yang sama muncul di dua objek panggilan fungsi, fungsi akan mengambil nilai variabel di objek panggilan terdekatnya:
Salinan kode adalah sebagai berikut:
fungsi f (x) {
var a = 10;
mengembalikan a*g (x);
fungsi g (b) {
var a = 1;
mengembalikan b*b*a;
}
}
console.log (f (6)); // 360, bukan 3600
Dalam contoh di atas, variabel A memiliki nilai yang berbeda dalam objek panggilan (g_invokeobj) dari fungsi g () dan objek panggilan (f_invokeObj) dari fungsi f (). Saat menjalankan fungsi g (), nilai yang digunakan di dalam fungsi g () adalah 1, sedangkan nilai yang digunakan di luar fungsi g () adalah 1. Fungsi rantai objek panggilan pada saat ini adalah sebagai berikut:
Apa itu penutupan?
Semua fungsi dalam JavaScript adalah objek, dan ketika mendefinisikan fungsi, rantai fungsi yang sesuai dengan objek panggilan akan dihasilkan. Definisi fungsi sesuai dengan rantai fungsi objek panggilan. Selama ada objek fungsi, ada objek panggilan fungsi yang sesuai; Setelah fungsi tidak lagi digunakan, objek panggilan fungsi yang sesuai akan dikumpulkan; dan kombinasi objek fungsi ini dan objek panggilan rantai fungsi disebut "penutupan". Dalam contoh fungsi fungsi f () di atas, ada dua penutupan: objek fungsi f () dan objek f_invokeobj membentuk penutupan, dan objek fungsi g () dan rantai objek G_invokeObjj-f_invokeObj membentuk penutupan kedua. Ketika fungsi G () dieksekusi, karena fungsi G () tidak lagi digunakan, penutupan G () dikumpulkan sampah; Kemudian, ketika fungsi f () dieksekusi, penutupan f () juga merupakan sampah yang dikumpulkan untuk alasan yang sama.
Dari definisi penutupan, kita dapat menarik kesimpulan: semua fungsi JavaScript adalah penutupan setelah definisi karena semua fungsi adalah objek, dan semua fungsi juga memiliki rantai objek panggilan yang sesuai setelah dieksekusi.
Namun, apa yang membuat penutupan benar -benar berhasil adalah kasus fungsi bersarang. Karena fungsi tertanam didefinisikan hanya ketika fungsi eksternal sedang berjalan, nilai variabel yang disimpan dalam penutupan fungsi tertanam (terutama nilai variabel lokal dari fungsi eksternal) adalah nilai selama menjalankan ini. Selama objek fungsi tertanam masih ada, penutupannya masih ada (nilai variabel dalam penutupan tidak akan mengubah apa pun), sehingga mencapai tujuan menyimpan informasi dari proses menjalankan fungsi. Pertimbangkan contoh berikut:
Salinan kode adalah sebagai berikut:
var a = "di luar";
fungsi f () {
var a = "di dalam";
fungsi g () {return a;}
kembali g;
}
var hasil = f ();
console.log (result ()); // di dalam
Dalam contoh ini, ketika fungsi f () dijalankan, fungsi g () didefinisikan, dan penutupan fungsi g () dibuat. Penutupan G () berisi rantai objek G_INVOKEOBJ-F_INVOKEOBJ, sehingga nilai variabel A selama eksekusi fungsi f () disimpan. Ketika pernyataan Console.log () dieksekusi, penutupan G () masih ada karena objek fungsi G masih ada; Saat menjalankan objek fungsi G yang masih ada ini, JavaScript akan menggunakan penutupan G () yang masih ada dan mendapatkan nilai variabel a ("di dalam") dari itu.