Singkatnya Javascript Closures memungkinkan suatu fungsi untuk mengakses variabel yang dideklarasikan dalam fungsi lexical-parent .
Mari kita lihat penjelasan yang lebih rinci. Untuk memahami penutupan, penting untuk memahami bagaimana JavaScript membatasi variabel.
Lingkup
Dalam JavaScript, lingkup didefinisikan dengan fungsi. Setiap fungsi menentukan ruang lingkup baru.
Perhatikan contoh berikut;
function f()
{//begin of scope f
var foo='hello'; //foo is declared in scope f
for(var i=0;i<2;i++){//i is declared in scope f
//the for loop is not a function, therefore we are still in scope f
var bar = 'Am I accessible?';//bar is declared in scope f
console.log(foo);
}
console.log(i);
console.log(bar);
}//end of scope f
memanggil f print
hello
hello
2
Am I Accessible?
Sekarang mari kita perhatikan kasus kita memiliki fungsi yang g
didefinisikan dalam fungsi lain f
.
function f()
{//begin of scope f
function g()
{//being of scope g
/*...*/
}//end of scope g
/*...*/
}//end of scope f
Kami akan memanggil f
para orang tua leksikal dari g
. Seperti yang dijelaskan sebelumnya kita sekarang memiliki 2 cakupan; ruang lingkup f
dan ruang lingkupg
.
Tetapi satu ruang lingkup "di dalam" ruang lingkup yang lain, jadi apakah ruang lingkup fungsi anak bagian dari ruang lingkup fungsi orang tua? Apa yang terjadi dengan variabel yang dideklarasikan dalam ruang lingkup fungsi induk; Apakah saya dapat mengaksesnya dari ruang lingkup fungsi anak? Di situlah penutupan terjadi.
Penutupan
Dalam JavaScript fungsi g
tidak hanya dapat mengakses variabel apa pun yang dideklarasikan dalam ruang lingkup g
tetapi juga mengakses variabel yang dideklarasikan dalam ruang lingkup fungsi indukf
.
Pertimbangkan untuk mengikuti;
function f()//lexical parent function
{//begin of scope f
var foo='hello'; //foo declared in scope f
function g()
{//being of scope g
var bar='bla'; //bar declared in scope g
console.log(foo);
}//end of scope g
g();
console.log(bar);
}//end of scope f
memanggil f print
hello
undefined
Mari kita lihat garisnya console.log(foo);
. Pada titik ini kami berada dalam cakupan g
dan kami mencoba mengakses variabel foo
yang dideklarasikan dalam lingkup f
. Tetapi seperti yang dinyatakan sebelumnya kita dapat mengakses variabel apa pun yang dideklarasikan dalam fungsi induk leksikal yang merupakan kasus di sini; g
adalah induk leksikal dari f
. Karena hello
itu dicetak.
Sekarang mari kita lihat garis console.log(bar);
. Pada titik ini kami berada dalam cakupan f
dan kami mencoba mengakses variabel bar
yang dideklarasikan dalam lingkup g
. bar
tidak dideklarasikan dalam ruang lingkup saat ini dan fungsinya g
bukan induk dari f
, oleh karena itubar
itu tidak terdefinisi
Sebenarnya kita juga dapat mengakses variabel yang dideklarasikan dalam ruang lingkup fungsi "grand parent" leksikal. Karena itu jika ada fungsi yang h
didefinisikan dalam fungsi tersebutg
function f()
{//begin of scope f
function g()
{//being of scope g
function h()
{//being of scope h
/*...*/
}//end of scope h
/*...*/
}//end of scope g
/*...*/
}//end of scope f
maka h
akan dapat mengakses semua variabel yang dideklarasikan dalam lingkup fungsi h
, g
dan f
. Ini dilakukan dengan penutupan . Dalam penutupan JavaScript memungkinkan kita untuk mengakses variabel apa pun yang dideklarasikan dalam fungsi induk leksikal, dalam fungsi grand parent leksikal, dalam fungsi grand-grand parent leksikal, dll. Ini dapat dilihat sebagai rantai lingkup ; scope of current function -> scope of lexical parent function -> scope of lexical grand parent function -> ...
sampai fungsi induk terakhir yang tidak memiliki induk leksikal.
Objek jendela
Sebenarnya rantai tidak berhenti pada fungsi induk terakhir. Ada satu ruang lingkup khusus lagi; yang lingkup global . Setiap variabel yang tidak dideklarasikan dalam suatu fungsi dianggap dideklarasikan dalam lingkup global. Ruang lingkup global memiliki dua spesialisasi;
- setiap variabel yang dinyatakan dalam lingkup global dapat diakses di mana saja
- variabel yang dideklarasikan dalam lingkup global sesuai dengan properti
window
objek.
Oleh karena itu ada dua cara untuk mendeklarasikan variabel foo
dalam lingkup global; baik dengan tidak mendeklarasikannya dalam suatu fungsi atau dengan mengatur properti foo
objek window.
Kedua upaya menggunakan penutupan
Sekarang setelah Anda membaca penjelasan yang lebih terperinci, kini mungkin tampak jelas bahwa kedua solusi menggunakan penutupan. Tapi yang pasti, mari kita buktikan.
Mari kita membuat Bahasa Pemrograman baru; JavaScript-Tanpa-Penutupan. Seperti namanya, JavaScript-No-Closure identik dengan JavaScript kecuali tidak mendukung Closures.
Dengan kata lain;
var foo = 'hello';
function f(){console.log(foo)};
f();
//JavaScript-No-Closure prints undefined
//JavaSript prints hello
Baiklah, mari kita lihat apa yang terjadi dengan solusi pertama dengan JavaScript-No-Closure;
for(var i = 0; i < 10; i++) {
(function(){
var i2 = i;
setTimeout(function(){
console.log(i2); //i2 is undefined in JavaScript-No-Closure
}, 1000)
})();
}
karena itu ini akan mencetak undefined
10 kali dalam JavaScript-No-Closure.
Oleh karena itu solusi pertama menggunakan penutupan.
Mari kita lihat solusi kedua;
for(var i = 0; i < 10; i++) {
setTimeout((function(i2){
return function() {
console.log(i2); //i2 is undefined in JavaScript-No-Closure
}
})(i), 1000);
}
karena itu ini akan mencetak undefined
10 kali dalam JavaScript-No-Closure.
Kedua solusi menggunakan penutupan.
Sunting: Diasumsikan bahwa 3 snippet kode ini tidak didefinisikan dalam lingkup global. Kalau tidak, variabel foo
dan i
akan mengikat ke window
objek dan karena itu dapat diakses melalui window
objek dalam JavaScript dan JavaScript-No-Closure