Cakupan adalah salah satu konsep terpenting dalam JavaScript. Jika Anda ingin mempelajari JavaScript dengan baik, Anda perlu memahami cara kerja cakupan dan rantai cakupan JavaScript. Artikel hari ini memberikan pengenalan singkat tentang cakupan dan rantai cakupan JavaScript, dengan harapan dapat membantu semua orang mempelajari JavaScript dengan lebih baik.
Ruang lingkup JavaScript
Bahasa pemrograman apa pun memiliki konsep cakupan. Sederhananya, cakupan adalah rentang variabel dan fungsi yang dapat diakses. Artinya, cakupan mengontrol visibilitas dan siklus hidup variabel dan fungsi. Dalam JavaScript, ada dua jenis cakupan variabel: cakupan global dan cakupan lokal.
1. Ruang Lingkup Global
Objek yang dapat diakses di mana saja dalam kode memiliki cakupan global. Secara umum, situasi berikut memiliki cakupan global:
(1) Fungsi terluar dan variabel yang didefinisikan di luar fungsi terluar mempunyai cakupan global, misalnya:
Copy kode kodenya sebagai berikut:
varauthorName="Aliran Tepi Gunung";
fungsi melakukan Sesuatu(){
varblogNama="";
fungsiinnerSay(){
peringatan(NamaBlog);
}
innerSay();
}
alert(authorName);//sungai di tepi gunung
peringatan(Namablog);//Kesalahan skrip
melakukan sesuatu();//
innerSay()//kesalahan skrip
(2) Semua variabel yang tidak didefinisikan dan ditetapkan secara langsung secara otomatis dinyatakan mempunyai cakupan global, misalnya:
Copy kode kodenya sebagai berikut:
fungsi melakukan Sesuatu(){
varauthorName="Aliran Tepi Gunung";
Nama blog="";
peringatan(NamaPenulis);
}
peringatan(NamaBlog);//
alert(namapenulis);//kesalahan skrip
Variabel blogName memiliki cakupan global, tetapi authorName tidak dapat diakses di luar fungsi.
(3) Semua properti objek jendela memiliki cakupan global
Umumnya, properti bawaan objek window memiliki cakupan global, seperti window.name, window.location, window.top, dll.
2. Ruang Lingkup Lokal
Berlawanan dengan lingkup global, lingkup lokal umumnya hanya dapat diakses dalam bagian kode tetap, paling sering dalam suatu fungsi, sehingga di beberapa tempat Anda juga akan melihat orang-orang menyebut lingkup ini sebagai lingkup fungsi, seperti kode berikut Keduanya blogName dan fungsi innerSay hanya memiliki cakupan lokal.
Copy kode kodenya sebagai berikut:
fungsi melakukan Sesuatu(){
varblogNama="";
fungsiinnerSay(){
peringatan(NamaBlog);
}
innerSay();
}
peringatan(Namablog);//Kesalahan skrip
innerSay();//kesalahan skrip
Rantai Lingkup
Dalam JavaScript, fungsi juga merupakan objek. Faktanya, semua yang ada di JavaScript adalah objek. Objek fungsi, seperti objek lainnya, memiliki properti yang dapat diakses melalui kode dan sekumpulan properti internal yang hanya dapat diakses oleh mesin JavaScript. Salah satu properti internal adalah [[Scope]], yang ditentukan oleh standar ECMA-262 edisi ketiga. Properti internal ini berisi kumpulan objek dalam cakupan tempat fungsi dibuat , yang menentukan data mana yang dapat diakses oleh fungsi tersebut.
Saat suatu fungsi dibuat, rantai cakupannya diisi dengan objek data yang dapat diakses dari cakupan tempat fungsi tersebut dibuat. Misalnya, tentukan fungsi berikut:
Copy kode kodenya sebagai berikut:
fungsitambahan(angka1,angka2){
varsum=angka1+angka2;
jumlah pengembalian;
}
Ketika fungsi add dibuat, rantai cakupannya akan diisi dengan objek global, yang berisi semua variabel global, seperti yang ditunjukkan pada gambar berikut (catatan: gambar hanya menggambarkan sebagian dari semua variabel):
Ruang lingkup penambahan fungsi akan digunakan selama eksekusi. Misalnya, jalankan kode berikut:
Copy kode kodenya sebagai berikut:
var total = tambah(5,10);
Ketika fungsi ini dijalankan, objek internal yang disebut "konteks eksekusi" dibuat. Konteks runtime mendefinisikan lingkungan di mana fungsi tersebut dijalankan. Setiap konteks runtime memiliki rantai cakupannya sendiri untuk resolusi pengidentifikasi. Ketika konteks runtime dibuat, rantai cakupannya diinisialisasi ke objek yang terdapat dalam [[Scope]] dari fungsi yang sedang berjalan.
Nilai-nilai tersebut disalin ke dalam rantai cakupan konteks runtime sesuai urutan kemunculannya dalam fungsi. Bersama-sama mereka membentuk objek baru yang disebut "objek aktivasi". Objek ini berisi semua variabel lokal, parameter bernama, kumpulan parameter, dan fungsi ini. Kemudian objek ini akan didorong ke ujung depan rantai cakupan dimusnahkan, maka objek aktif juga ikut musnah. Rantai cakupan baru ditunjukkan di bawah ini:
Selama eksekusi fungsi, jika suatu variabel tidak ditemukan, maka variabel tersebut akan melalui proses resolusi pengenal untuk menentukan di mana memperoleh dan menyimpan data. Proses ini dimulai dari kepala rantai cakupan, yaitu dari objek aktif, dan mencari pengidentifikasi dengan nama yang sama, jika ditemukan, gunakan variabel yang sesuai dengan pengidentifikasi tersebut mencari objek berikutnya dalam rantai cakupan. Jika tidak ada objek yang ditemukan setelah pencarian, pengidentifikasi dianggap tidak terdefinisi. Selama eksekusi fungsi, setiap pengidentifikasi menjalani proses pencarian seperti itu.
Rangkaian cakupan dan pengoptimalan kode
Terlihat dari struktur rantai lingkup bahwa semakin dalam pengidentifikasi berada dalam rantai lingkup konteks runtime, maka kecepatan membaca dan menulis akan semakin lambat. Seperti yang ditunjukkan pada gambar di atas, karena variabel global selalu ada di akhir rantai cakupan konteks runtime, pencarian variabel global adalah yang paling lambat selama penyelesaian pengidentifikasi. Oleh karena itu, saat menulis kode, Anda harus menggunakan variabel global sesedikit mungkin dan menggunakan variabel lokal sebanyak mungkin. Aturan praktis yang baik adalah: jika objek lintas cakupan direferensikan lebih dari satu kali, simpanlah dalam variabel lokal sebelum menggunakannya. Misalnya kode berikut:
Copy kode kodenya sebagai berikut:
fungsiperubahanWarna(){
document.getElementById("btnChange").onclick=function(){
document.getElementById("targetCanvas").style.backgroundColor="merah";
};
}
Fungsi ini mereferensikan dokumen variabel global dua kali. Untuk menemukan variabel, Anda harus melintasi seluruh rantai cakupan hingga akhirnya ditemukan di objek global. Kode ini dapat ditulis ulang sebagai berikut:
Copy kode kodenya sebagai berikut:
fungsiperubahanWarna(){
vardoc=dokumen;
doc.getElementById("btnChange").onclick=function(){
doc.getElementById("targetCanvas").style.backgroundColor="merah";
};
}
Kode ini relatif sederhana dan tidak akan menunjukkan peningkatan kinerja yang besar setelah ditulis ulang. Namun, jika ada sejumlah besar variabel global dalam program yang diakses berulang kali, kinerja kode yang ditulis ulang akan meningkat secara signifikan.
mengubah rantai cakupan
Konteks runtime yang terkait bersifat unik setiap kali suatu fungsi dijalankan, jadi memanggil fungsi yang sama beberapa kali akan menghasilkan pembuatan beberapa konteks runtime. Saat fungsi menyelesaikan eksekusi, konteks eksekusi akan dimusnahkan. Setiap konteks runtime dikaitkan dengan rantai cakupan. Dalam keadaan normal, selama eksekusi konteks runtime, rantai cakupannya hanya akan dipengaruhi oleh pernyataan with dan pernyataan catch.
Pernyataan with adalah cara pintas untuk menggunakan objek untuk menghindari penulisan kode berulang. Misalnya:
Copy kode kodenya sebagai berikut:
fungsiinitUI(){
dengan(dokumen){
varbd=tubuh,
tautan=getElementsByTagName("a"),
saya=0,
len=link.panjang;
sementara(saya<len){
perbarui(tautan[i++]);
}
getElementById("btnInit").onclick=function(){
melakukan sesuatu();
};
}
}
Pernyataan lebar digunakan di sini untuk menghindari penulisan dokumen berkali-kali, yang tampaknya lebih efisien, namun sebenarnya menyebabkan masalah kinerja.
Ketika kode mencapai pernyataan with, rantai cakupan konteks runtime diubah untuk sementara. Objek baru yang bisa diubah dibuat yang berisi semua properti objek yang ditentukan oleh parameter. Objek ini akan didorong ke bagian atas rantai cakupan, yang berarti bahwa semua variabel lokal dari fungsi tersebut sekarang berada di objek rantai cakupan kedua, dan oleh karena itu lebih mahal untuk diakses. Seperti yang ditunjukkan di bawah ini:
Oleh karena itu, Anda harus menghindari penggunaan pernyataan with dalam program Anda, dalam contoh ini, menyimpan dokumen dalam variabel lokal dapat meningkatkan kinerja.
Hal lain yang mengubah rantai cakupan adalah pernyataan catch dalam pernyataan try-catch. Ketika kesalahan terjadi di blok kode coba, proses eksekusi melompat ke pernyataan catch, dan kemudian objek pengecualian didorong ke objek yang bisa diubah dan ditempatkan di bagian atas ruang lingkup. Di dalam blok catch, semua variabel lokal dari fungsi tersebut akan ditempatkan di objek rantai lingkup kedua. Contoh kode:
Copy kode kodenya sebagai berikut:
mencoba{
melakukan sesuatu();
}menangkap(misal){
alert(ex.message);//Rantai cakupan berubah di sini
}
Harap dicatat bahwa setelah pernyataan catch dijalankan, rantai cakupan akan kembali ke keadaan sebelumnya. Pernyataan try-catch sangat berguna dalam debugging kode dan penanganan pengecualian, sehingga tidak disarankan untuk menghindarinya sepenuhnya. Anda dapat mengurangi dampak kinerja pernyataan catch dengan mengoptimalkan kode Anda. Pola yang baik adalah mendelegasikan penanganan kesalahan ke suatu fungsi, misalnya:
Copy kode kodenya sebagai berikut:
mencoba{
melakukan sesuatu();
}menangkap(misal){
handleError(ex);//Delegasi ke metode prosesor
}
Dalam kode yang dioptimalkan, metode handleError adalah satu-satunya kode yang dieksekusi dalam klausa catch. Fungsi ini menerima objek pengecualian sebagai parameter, sehingga Anda dapat menangani kesalahan dengan lebih fleksibel dan seragam. Karena hanya satu pernyataan yang dieksekusi dan tidak ada variabel lokal yang diakses, perubahan sementara dalam rantai cakupan tidak akan mempengaruhi kinerja kode.