Cakupan
Lingkup adalah ruang lingkup fungsi variabel dan fungsi. Semua variabel yang dinyatakan dalam suatu fungsi dalam JavaScript selalu terlihat di badan fungsi. Ada lingkup global dan lingkup lokal di JavaScript, tetapi tidak ada ruang lingkup level blok. Prioritas variabel lokal lebih tinggi dari variabel global. Melalui beberapa contoh, kita dapat memahami "aturan yang tidak terucapkan" ruang lingkup dalam JavaScript (ini juga pertanyaan yang sering ditanyakan dalam wawancara depan).
1. Deklarasi variabel sebelumnya
Contoh 1:
var scope = "global"; function scopetest () {console.log (scope); var scope = "local"} scopetest (); //belum diartikanOutput di sini tidak terdefinisi dan tidak ada kesalahan. Ini karena deklarasi dalam fungsi yang kami sebutkan di atas selalu terlihat di badan fungsi. Fungsi di atas setara dengan:
var scope = "global"; function scopetest () {var scope; console.log (lingkup); scope = "local"} scopetest (); //lokalPerhatikan bahwa jika VAR dilupakan, variabel dinyatakan sebagai variabel global.
2. Tidak ada ruang lingkup level blok
Tidak seperti bahasa lain yang biasa kami gunakan, tidak ada ruang lingkup level blok dalam JavaScript:
function scopetest () {var scope = {}; if (scope instance dari objek) {var j = 1; untuk (var i = 0; i <10; i ++) {//console.log(i); } console.log (i); // output 10} console.log (j); // output 1}Dalam JavaScript, ruang lingkup fungsi variabel adalah tingkat fungsi, yaitu, semua variabel dalam fungsi didefinisikan dalam seluruh fungsi, yang juga membawa beberapa "aturan yang tidak terucapkan" yang akan kita temui jika kita tidak hati-hati:
var scope = "hello"; function scopetest () {console.log (scope); // ① var scope = "no"; console.log (scope); // ②}Output nilai pada ① sebenarnya tidak terdefinisi, yang gila. Kami telah menentukan nilai variabel global. Bukankah seharusnya tempat ini halo? Faktanya, kode di atas setara dengan:
var scope = "hello"; function scopetest () {var scope; console.log (scope); // ① scope = "no"; console.log (scope); // ②}Deklarasikan variabel awal dan global memiliki prioritas yang lebih rendah daripada variabel lokal. Menurut dua aturan ini, tidak sulit untuk memahami mengapa output tidak ditentukan.
Rantai Lingkup
Dalam JavaScript, setiap fungsi memiliki konteks eksekusi sendiri. Ketika kode dieksekusi di lingkungan ini, rantai lingkup objek variabel akan dibuat. Rantai lingkup adalah daftar objek atau rantai objek, yang memastikan akses tertib ke objek variabel.
Ujung depan rantai lingkup adalah objek variabel dari lingkungan eksekusi kode saat ini, yang sering disebut "objek aktif". Variabel pencarian dimulai dari objek rantai pertama. Jika objek berisi atribut variabel, maka pencarian akan dihentikan. Jika tidak, pencarian akan terus mencari rantai lingkup superior sampai objek global ditemukan:
Pencarian untuk rantai lingkup langkah demi langkah juga akan mempengaruhi kinerja program. Semakin lama rantai ruang lingkup variabel, semakin besar dampaknya pada kinerja. Ini juga merupakan alasan utama mengapa kami mencoba menghindari penggunaan variabel global.
Penutup
Konsep Dasar
Lingkup adalah prasyarat untuk memahami penutupan. Penutupan merujuk pada kemampuan untuk mengakses variabel dalam ruang lingkup eksternal dalam ruang lingkup saat ini.
fungsi createClosure () {var name = "jack"; return {setStr: function () {name = "rose"; }, getStr: function () {return name + ": hello"; }}} var builder = createClosure baru (); builder.setstr (); console.log (builder.getstr ()); // Rose: HaloContoh di atas mengembalikan dua penutupan dalam fungsi, yang keduanya mempertahankan referensi ke ruang lingkup eksternal, sehingga variabel dalam fungsi eksternal selalu dapat diakses di mana pun mereka dipanggil. Fungsi yang didefinisikan di dalam suatu fungsi akan menambahkan objek aktif dari fungsi eksternal ke rantai ruang lingkupnya sendiri. Oleh karena itu, dalam contoh di atas, fungsi internal dapat mengakses sifat fungsi eksternal melalui fungsi internal. Ini juga merupakan cara bagi JavaScript untuk mensimulasikan variabel pribadi.
Catatan: Karena penutupan akan memiliki ruang lingkup fungsi tambahan (fungsi anonim internal membawa ruang lingkup fungsi eksternal), penutupan akan menempati lebih banyak ruang memori daripada fungsi lain, dan penggunaan berlebihan dapat menyebabkan peningkatan penggunaan memori.
Variabel dalam penutupan
Saat menggunakan penutupan, karena pengaruh mekanisme rantai ruang lingkup, penutupan hanya dapat memperoleh nilai terakhir dari fungsi internal. Salah satu efek samping dari ini adalah bahwa jika fungsi internal berada dalam loop, nilai variabel selalu merupakan nilai terakhir.
// Contoh ini tidak masuk akal dan memiliki faktor penundaan tertentu. Ini terutama untuk mengilustrasikan masalah dalam fungsi loop penutupan timemanage () {for (var i = 0; i <5; i ++) {setTimeout (function () {console.log (i);}, 1000)}; }Program di atas tidak memasukkan angka 1-5 seperti yang kami harapkan, tetapi menghasilkan 5 semua 5 kali. Mari kita lihat contoh lain:
fungsi createClosure () {var result = []; untuk (var i = 0; i <5; i ++) {result [i] = function () {return i; }} return hasil;}Memanggil createClosure () [0] () mengembalikan 5, dan createClosure () [4] () Nilai pengembalian masih 5. Dari dua contoh di atas, kita dapat melihat masalah bahwa penutupan ada saat menggunakan fungsi internal dengan loop: karena rantai ruang lingkup setiap fungsi menyimpan objek aktif untuk fungsi eksternal (timemanage, createclosure), mereka merujuk pada rujukan ke variasi yang sama. Ketika fungsi eksternal kembali, nilai I pada saat ini adalah 5, jadi nilai setiap fungsi internal I juga 5.
Jadi bagaimana cara menyelesaikan masalah ini? Kita dapat memaksa kembalinya hasil yang diharapkan melalui pembungkus anonim (ekspresi fungsi pengukuhan diri anonim):
function timeManage () {for (var i = 0; i <5; i ++) {(function (num) {setTimeOut (function () {console.log (num);}, 1000);}) (i); }}Atau kembalikan penugasan fungsi anonim dalam fungsi anonim penutupan:
function timemanage () {for (var i = 0; i <10; i ++) {setTimeOut ((function (e) {return function () {console.log (e);}}) (i), 1000)}} // timeManager (); Output 1,2,3,4,5Function createClosure () {var result = []; untuk (var i = 0; i <5; i ++) {hasil [i] = fungsi (num) {return function () {console.log (num); } }(Saya); } return hasil;} // createClosure () [1] () output 1; CreateClosure () [2] () Output 2Apakah itu pembungkus anonim atau fungsi anonim bersarang, pada prinsipnya, karena fungsi tersebut dilewatkan berdasarkan nilai, nilai variabel saya akan disalin ke parameter aktual NUM, dan fungsi anonim dibuat di dalam fungsi anonim untuk mengembalikan NUM, sehingga setiap fungsi memiliki salinan NUM, yang tidak akan mempengaruhi satu sama lain.
Ini sebagai penutup
Berikan perhatian khusus saat menggunakan ini sebagai penutupan, karena sedikit kecerobohan dapat menyebabkan masalah. Biasanya kami memahami bahwa objek ini terikat oleh fungsi saat runtime. Dalam fungsi global, objek ini adalah objek jendela. Ketika fungsi disebut sebagai metode dalam objek, ini sama dengan objek ini (Todo melakukan proses penyortiran tentang ini). Karena ruang lingkup fungsi anonim adalah global, penutupan ini biasanya menunjuk ke jendela objek global:
var scope = "global"; var objek = {scope: "local", getscope: function () {return function () {return this.scope; }}}Calling Object.getScope () () Mengembalikan nilai global alih -alih lokal yang kami harapkan. Kami mengatakan sebelumnya bahwa fungsi anonim internal dalam penutupan akan membawa ruang lingkup fungsi eksternal, jadi mengapa tidak mendapatkan ini dari fungsi eksternal? Ketika setiap fungsi dipanggil, ini dan argumen akan dibuat secara otomatis. Saat mencari fungsi anonim internal, mereka mencari variabel yang kami inginkan di objek aktif. Oleh karena itu, berhenti mencari fungsi eksternal, dan tidak mungkin untuk secara langsung mengakses variabel dalam fungsi eksternal. Singkatnya, ketika suatu fungsi disebut sebagai metode objek dalam penutupan, penting untuk memberikan perhatian khusus bahwa ini dalam fungsi anonim dalam metode menunjuk ke variabel global.
Untungnya, kita dapat menyelesaikan masalah ini dengan sangat sederhana, cukup simpan ini di ruang lingkup fungsi eksternal dalam variabel yang dapat diakses dengan penutupan:
var scope = "global"; var objek = {scope: "local", getscope: function () {var that = this; return function () {return that.scope; }}} object.getScope () () () Mengembalikan nilai lokal.Memori dan kinerja
Karena penutupan berisi referensi rantai ruang lingkup yang sama dengan konteks runtime fungsi, ia akan memiliki efek negatif tertentu. Ketika objek aktif dan konteks runtime dalam fungsi dihancurkan, objek aktif tidak dapat dihancurkan karena masih ada referensi ke objek aktif, yang berarti bahwa penutupan menempati lebih banyak ruang memori daripada fungsi biasa, dan juga dapat menyebabkan kebocoran memori di browser IE, sebagai berikut:
fungsi bindevent () {var target = document.geteLementById ("elem"); target.onClick = function () {console.log (target.name); }}Dalam contoh di atas, fungsi anonim menghasilkan referensi ke target objek eksternal. Selama fungsi anonim ada, referensi tidak akan hilang, dan objek target fungsi eksternal tidak akan dihancurkan, yang menciptakan referensi melingkar. Solusinya adalah mengurangi referensi melingkar ke variabel eksternal dengan membuat salinan target.
fungsi bindevent () {var target = document.geteLementById ("elem"); var name = target.name; target.onClick = function () {console.log (name); } target = null; }Jika ada akses ke variabel eksternal dalam penutupan, jalur pencarian untuk pengidentifikasi pasti akan ditambahkan, dan dalam keadaan tertentu, ini juga akan menyebabkan kerugian kinerja. Kami telah menyebutkan sebelumnya: Cobalah untuk menyimpan variabel eksternal ke dalam variabel lokal untuk mengurangi panjang pencarian rantai lingkup.
Ringkasan: Penutupan tidak unik untuk JavaScript, tetapi mereka memiliki manifestasi unik mereka sendiri dalam JavaScript. Menggunakan penutupan, kita dapat mendefinisikan beberapa variabel pribadi dalam JavaScript, dan bahkan meniru lingkup level blok. Namun, selama penggunaan penutupan, kita juga perlu memahami masalah yang ada untuk menghindari masalah yang tidak perlu.