Jika Anda seorang pemrogram JavaScript pemula, atau jika Anda telah mengutak-atik JQuery untuk melakukan beberapa animasi di situs web Anda, kemungkinan Anda kehilangan beberapa bagian penting dari pengetahuan tentang JavaScript
Salah satu konsep terpenting adalah bagaimana ruang lingkup mengikat "ini"
Untuk posting ini, saya akan berasumsi bahwa Anda memiliki pemahaman yang baik tentang sintaks/objek dasar JavaScript dan terminologi umum saat mendiskusikan ruang lingkup [blok vs. lingkup fungsi, kata kunci ini, leksikal vs. pelingkupan dinamis]
Lingkup Leksikal
Pertama, JavaScript memiliki pelingkupan leksikal dengan cakupan fungsi. Dengan kata lain, meskipun JavaScript sepertinya harus memiliki ruang lingkup blok karena menggunakan kurung kurawal {}, ruang lingkup baru dibuat hanya ketika Anda membuat fungsi baru
var outerFunction = function[]{ if[true]{ var x = 5; //console.log[y]; //line 1, ReferenceError: y not defined } var nestedFunction = function[] { if[true]{ var y = 7; console.log[x]; //line 2, x will still be known prints 5 } if[true]{ console.log[y]; //line 3, prints 7 } } return nestedFunction; } var myFunction = outerFunction[]; myFunction[];
Dalam contoh ini, variabel x tersedia di mana saja di dalam outerFunction[]
. Juga, variabel y tersedia di mana saja dalam nestedFunction[]
, tetapi tidak ada yang tersedia di luar fungsi tempat mereka didefinisikan. Alasan untuk ini dapat dijelaskan dengan pelingkupan leksikal. Cakupan variabel ditentukan oleh posisinya dalam kode sumber. Untuk menyelesaikan variabel, JavaScript dimulai dari lingkup terdalam dan mencari keluar hingga menemukan variabel yang dicari. Pelingkupan leksikal bagus, karena kita dapat dengan mudah mengetahui nilai suatu variabel dengan melihat kodenya;
Penutupan
Fakta bahwa kita dapat mengakses variabel x mungkin masih membingungkan, karena biasanya variabel lokal di dalam suatu fungsi hilang setelah suatu fungsi selesai dieksekusi. Kami memanggil outerFunction[]
dan menugaskan hasilnya, nestedFunction[]
, ke
var cat = { name: "Gus", color: "gray", age: 15, printInfo: function[] { console.log["Name:", this.name, "Color:", this.color, "Age:", this.age]; //line 1, prints correctly nestedFunction = function[] { console.log["Name:", this.name, "Color:", this.color, "Age:", this.age]; //line 2, loses cat scope } nestedFunction[]; } } cat.printInfo[]; //prints Name: window Color: undefined Age: undefined0. Bagaimana variabel x masih ada jika
outerFunction[]
telah kembali?Hanya mengakses variabel di luar lingkup langsung [tidak diperlukan pernyataan pengembalian] akan membuat sesuatu yang disebut penutupan. Jaringan Pengembangan Mozilla [MDN] memberikan definisi yang bagus
“Penutupan adalah jenis objek khusus yang menggabungkan dua hal. fungsi, dan lingkungan di mana fungsi itu dibuat. Lingkungan terdiri dari variabel lokal apa pun yang berada dalam ruang lingkup pada saat penutupan dibuat. ”
Karena x adalah anggota lingkungan yang membuat nestedFunction[]
, nestedFunction[]
akan memiliki akses ke sana. Cukup mudah? . Coba contoh ini, yang memiliki fungsi bersarang di dalam objek dengan beberapa properti
var cat = { name: "Gus", color: "gray", age: 15, printInfo: function[] { console.log["Name:", this.name, "Color:", this.color, "Age:", this.age]; //line 1, prints correctly nestedFunction = function[] { console.log["Name:", this.name, "Color:", this.color, "Age:", this.age]; //line 2, loses cat scope } nestedFunction[]; } } cat.printInfo[]; //prints Name: window Color: undefined Age: undefined_
Mengapa warna dan usia tidak ditentukan pada baris 2?
JavaScript kehilangan cakupan ini saat digunakan di dalam fungsi yang terdapat di dalam fungsi lain. Ketika hilang, secara default, ini akan terikat ke objek jendela global. Dalam contoh kami, kebetulan objek jendela juga memiliki properti "nama" dengan nilai "jendela"
Mengontrol Konteks
Jadi bagaimana sekarang?
Kami tidak dapat mengubah cara kerja pelingkupan leksikal dalam JavaScript, tetapi kami dapat mengontrol konteks di mana kami memanggil fungsi kami. Konteks diputuskan saat runtime saat fungsi dipanggil, dan selalu terikat ke objek di mana fungsi dipanggil. Satu-satunya pengecualian untuk aturan ini adalah kasus fungsi bersarang di atas
Dengan mengatakan, ubah "konteks", maksud saya, kami mengubah apa sebenarnya "ini". Pada contoh di bawah ini, apa yang dicetak oleh “baris 1” dan “baris 2”?
var obj1 = { printThis: function[] { console.log[this]; } }; var func1 = obj1.printThis; obj1.printThis[]; //line 1 func1[]; //line 2
Baris 1 mencetak obj1. Baris 2 mencetak jendela. Konteks baris 1 adalah obj1, karena kami segera memanggil
var cat = { name: "Gus", color: "gray", age: 15, printInfo: function[] { console.log["Name:", this.name, "Color:", this.color, "Age:", this.age]; //line 1, prints correctly nestedFunction = function[] { console.log["Name:", this.name, "Color:", this.color, "Age:", this.age]; //line 2, loses cat scope } nestedFunction[]; } } cat.printInfo[]; //prints Name: window Color: undefined Age: undefined4. Namun, di
var cat = { name: "Gus", color: "gray", age: 15, printInfo: function[] { console.log["Name:", this.name, "Color:", this.color, "Age:", this.age]; //line 1, prints correctly nestedFunction = function[] { console.log["Name:", this.name, "Color:", this.color, "Age:", this.age]; //line 2, loses cat scope } nestedFunction[]; } } cat.printInfo[]; //prints Name: window Color: undefined Age: undefined_5, pertama-tama kita menyimpan referensi ke fungsi
var cat = { name: "Gus", color: "gray", age: 15, printInfo: function[] { console.log["Name:", this.name, "Color:", this.color, "Age:", this.age]; //line 1, prints correctly nestedFunction = function[] { console.log["Name:", this.name, "Color:", this.color, "Age:", this.age]; //line 2, loses cat scope } nestedFunction[]; } } cat.printInfo[]; //prints Name: window Color: undefined Age: undefined4, lalu memanggilnya dalam konteks objek global; . Jika
var cat = { name: "Gus", color: "gray", age: 15, printInfo: function[] { console.log["Name:", this.name, "Color:", this.color, "Age:", this.age]; //line 1, prints correctly nestedFunction = function[] { console.log["Name:", this.name, "Color:", this.color, "Age:", this.age]; //line 2, loses cat scope } nestedFunction[]; } } cat.printInfo[]; //prints Name: window Color: undefined Age: undefined_5 telah disarangkan dalam fungsi yang berbeda, itu akan menjadi konteksnya
Panggil, Ikat, dan Terapkan
Ada beberapa cara untuk mengontrol nilai ini, termasuk yang berikut ini
- menyimpan referensi untuk ini di variabel lain
- panggilan[]
- menerapkan[]
- mengikat[]
Yang pertama
var cat = { name: "Gus", color: "gray", age: 15, printInfo: function[] { var that = this; console.log["Name:", this.name, "Color:", this.color, "Age:", this.age]; //prints correctly nestedFunction = function[] { console.log["Name:", that.name, "Color:", that.color, "Age:", that.age]; //prints correctly } nestedFunction[]; } } cat.printInfo[];_
Karena kami mengikat ini ke variabel itu, itu akan tersedia seperti variabel lainnya. Mari kita lihat sekarang
var cat = { name: "Gus", color: "gray", age: 15, printInfo: function[] { console.log["Name:", this.name, "Color:", this.color, "Age:", this.age]; //line 1, prints correctly nestedFunction = function[] { console.log["Name:", this.name, "Color:", this.color, "Age:", this.age]; //line 2, loses cat scope } nestedFunction[]; } } cat.printInfo[]; //prints Name: window Color: undefined Age: undefined_8,
var cat = { name: "Gus", color: "gray", age: 15, printInfo: function[] { console.log["Name:", this.name, "Color:", this.color, "Age:", this.age]; //line 1, prints correctly nestedFunction = function[] { console.log["Name:", this.name, "Color:", this.color, "Age:", this.age]; //line 2, loses cat scope } nestedFunction[]; } } cat.printInfo[]; //prints Name: window Color: undefined Age: undefined9, dan
var obj1 = { printThis: function[] { console.log[this]; } }; var func1 = obj1.printThis; obj1.printThis[]; //line 1 func1[]; //line 20, yang menurut saya sedikit lebih bersih
var cat = { name: "Gus", color: "gray", age: 15, printInfo: function[] { console.log["Name:", this.name, "Color:", this.color, "Age:", this.age]; nestedFunction = function[] { console.log["Name:", this.name, "Color:", this.color, "Age:", this.age]; } nestedFunction.call[this]; nestedFunction.apply[this]; var storeFunction = nestedFunction.bind[this]; storeFunction[]; } } cat.printInfo[];
Fokus pada argumen pertama dari setiap fungsi. ini. Ini mengacu pada objek kucing. Sekarang, nestedFunction[]
_ ini mengacu pada objek kucing. Jika, alih-alih menyampaikan ini sebagai argumen, kami meneruskan objek "anjing", maka nestedFunction[]
ini mengacu pada anjing. Pada dasarnya, apa pun argumen pertama menjadi Fungsi bersarang [] adalah ini
Jadi apa perbedaan antara ketiganya?
Perbedaan utama antara
var cat = { name: "Gus", color: "gray", age: 15, printInfo: function[] { console.log["Name:", this.name, "Color:", this.color, "Age:", this.age]; //line 1, prints correctly nestedFunction = function[] { console.log["Name:", this.name, "Color:", this.color, "Age:", this.age]; //line 2, loses cat scope } nestedFunction[]; } } cat.printInfo[]; //prints Name: window Color: undefined Age: undefined8 dan
var cat = { name: "Gus", color: "gray", age: 15, printInfo: function[] { console.log["Name:", this.name, "Color:", this.color, "Age:", this.age]; //line 1, prints correctly nestedFunction = function[] { console.log["Name:", this.name, "Color:", this.color, "Age:", this.age]; //line 2, loses cat scope } nestedFunction[]; } } cat.printInfo[]; //prints Name: window Color: undefined Age: undefined9 terletak pada cara menyampaikan argumen tambahan.
var obj1 = { printThis: function[] { console.log[this]; } }; var func1 = obj1.printThis; obj1.printThis[]; //line 1 func1[]; //line 25 mengambil beberapa argumen, dipisahkan dengan koma, yang memungkinkan
nestedFunction[]
untuk memanfaatkannya. var obj1 = { printThis: function[] { console.log[this]; } }; var func1 = obj1.printThis; obj1.printThis[]; //line 1 func1[]; //line 27 juga mengambil argumen tambahan dengan cara ini. Namun,
var cat = { name: "Gus", color: "gray", age: 15, printInfo: function[] { console.log["Name:", this.name, "Color:", this.color, "Age:", this.age]; //line 1, prints correctly nestedFunction = function[] { console.log["Name:", this.name, "Color:", this.color, "Age:", this.age]; //line 2, loses cat scope } nestedFunction[]; } } cat.printInfo[]; //prints Name: window Color: undefined Age: undefined_9 menggunakan satu larik argumen, bukan beberapa argumen
Penting untuk diingat bahwa menggunakan
var cat = { name: "Gus", color: "gray", age: 15, printInfo: function[] { console.log["Name:", this.name, "Color:", this.color, "Age:", this.age]; //line 1, prints correctly nestedFunction = function[] { console.log["Name:", this.name, "Color:", this.color, "Age:", this.age]; //line 2, loses cat scope } nestedFunction[]; } } cat.printInfo[]; //prints Name: window Color: undefined Age: undefined8 dan
var cat = { name: "Gus", color: "gray", age: 15, printInfo: function[] { console.log["Name:", this.name, "Color:", this.color, "Age:", this.age]; //line 1, prints correctly nestedFunction = function[] { console.log["Name:", this.name, "Color:", this.color, "Age:", this.age]; //line 2, loses cat scope } nestedFunction[]; } } cat.printInfo[]; //prints Name: window Color: undefined Age: undefined9 sebenarnya memanggil fungsi Anda — jadi ini salah [perhatikan [] pada nestedFunction]
nestedFunction[].call[this];
var obj1 = { printThis: function[] { console.log[this]; } }; var func1 = obj1.printThis; obj1.printThis[]; //line 1 func1[]; //line 27, di sisi lain, bagus karena memungkinkan Anda untuk mengubah apa referensi ini dan kemudian menyimpan referensi ke fungsi yang diubah dalam variabel untuk digunakan di lain waktu [lihat storeFunction dalam contoh kode di atas]. Sementara itu, karena
var cat = { name: "Gus", color: "gray", age: 15, printInfo: function[] { console.log["Name:", this.name, "Color:", this.color, "Age:", this.age]; //line 1, prints correctly nestedFunction = function[] { console.log["Name:", this.name, "Color:", this.color, "Age:", this.age]; //line 2, loses cat scope } nestedFunction[]; } } cat.printInfo[]; //prints Name: window Color: undefined Age: undefined8 dan
var cat = { name: "Gus", color: "gray", age: 15, printInfo: function[] { console.log["Name:", this.name, "Color:", this.color, "Age:", this.age]; //line 1, prints correctly nestedFunction = function[] { console.log["Name:", this.name, "Color:", this.color, "Age:", this.age]; //line 2, loses cat scope } nestedFunction[]; } } cat.printInfo[]; //prints Name: window Color: undefined Age: undefined9 segera menjalankan fungsi tersebut, mereka mengembalikan hasil pemanggilan fungsi tersebut
Aplikasi praktis
Sejauh ini, kami telah melihat penutupan,
var cat = { name: "Gus", color: "gray", age: 15, printInfo: function[] { console.log["Name:", this.name, "Color:", this.color, "Age:", this.age]; //line 1, prints correctly nestedFunction = function[] { console.log["Name:", this.name, "Color:", this.color, "Age:", this.age]; //line 2, loses cat scope } nestedFunction[]; } } cat.printInfo[]; //prints Name: window Color: undefined Age: undefined8,
var cat = { name: "Gus", color: "gray", age: 15, printInfo: function[] { console.log["Name:", this.name, "Color:", this.color, "Age:", this.age]; //line 1, prints correctly nestedFunction = function[] { console.log["Name:", this.name, "Color:", this.color, "Age:", this.age]; //line 2, loses cat scope } nestedFunction[]; } } cat.printInfo[]; //prints Name: window Color: undefined Age: undefined9, dan
var obj1 = { printThis: function[] { console.log[this]; } }; var func1 = obj1.printThis; obj1.printThis[]; //line 1 func1[]; //line 20, tetapi kami belum membahas aplikasi praktis apa pun dan kapan harus menggunakan masing-masing untuk mendapatkan pengikatan yang benar dari ini