Apa itu
Dalam JavaScript, setiap fungsi, ketika dipanggil, menciptakan konteks eksekusi baru. Karena variabel dan fungsi yang didefinisikan dalam fungsi adalah satu -satunya variabel yang diakses secara internal, tidak secara eksternal, ketika memanggil fungsi, konteks yang disediakan oleh fungsi memberikan cara yang sangat sederhana untuk membuat variabel pribadi.
fungsi makeCounter () {var i = 0; return function () {console.log (++ i); }; } // Ingat: `counter` dan` counter2` memiliki variabel sendiri `i`var counter = makeCounter (); counter (); // 1counter (); // 2var counter2 = makeCounter (); counter2 (); // 1counter2 (); // 2i; // Referenceerror: I bukan lefined () () (); // 2i; // Referenceerror: I doning: ITEUNTER2 (); // 2i; // Referenceerror: I doning: ITISTER ();Dalam banyak kasus, Anda mungkin tidak memerlukan fungsi seperti membuat apa pun untuk mengembalikan beberapa nilai akumulasi, dan Anda hanya dapat menyebutnya sekali untuk mendapatkan nilai tunggal, dan dalam beberapa kasus lain Anda bahkan tidak perlu mengetahui nilai pengembalian secara eksplisit.
Intinya
Sekarang, tidak masalah Anda mendefinisikan fungsi seperti fungsi foo () {} atau var foo = function () {}, saat menelepon, Anda perlu menambahkan sepasang tanda kurung setelahnya, seperti foo ().
//The function defined below can be called by adding a pair of brackets after the function name, like `foo()`, //Because foo is just a reference variable var foo = function(){/* code */}` relative to the function expression `function(){/* code */}` var foo = function(){/* code */}// Then this shows that function expression can be called by adding a pair of kurung setelah itu? function () { / * code * /} (); // sintakser: token tak terduga (Seperti yang Anda lihat, bug ditangkap di sini. Ketika tanda kurung muncul di belakang fungsi untuk memanggil fungsi, apakah Anda menemukan kata kunci fungsi seperti itu di lingkungan global atau lingkungan lokal, secara default, itu akan memperlakukannya sebagai deklarasi fungsi daripada ekspresi fungsi. Jika Anda tidak secara eksplisit memberi tahu tanda kurung bahwa itu adalah ekspresi, itu akan memperlakukannya sebagai fungsi tanpa nama dan melempar kesalahan, karena deklarasi fungsi memerlukan nama.
Pertanyaan 1: Dapatkah saya memikirkan pertanyaan di sini? Bisakah kita juga memanggil fungsi var foo = function () {console.log (1)} () secara langsung seperti ini, dan jawabannya ok.
Pertanyaan 2: Demikian pula, kita juga bisa memikirkan pertanyaan. Jika deklarasi fungsi seperti ini dipanggil langsung dengan tanda kurung setelah itu, apa yang terjadi? Silakan lihat jawabannya di bawah.
Fungsi, tanda kurung, kesalahan
Menariknya, jika Anda menentukan nama untuk suatu fungsi dan menempatkan sepasang tanda kurung di belakangnya, kesalahan yang sama akan dilemparkan, tetapi kali ini karena alasan lain. Ketika tanda kurung ditempatkan setelah ekspresi fungsi, itu menunjukkan bahwa ini adalah fungsi yang dipanggil, dan tanda kurung ditempatkan setelah deklarasi, itu berarti bahwa mereka sepenuhnya dipisahkan dari deklarasi fungsi sebelumnya. Pada saat ini, tanda kurung hanyalah representasi sederhana dari tanda kurung (kawat gigi yang digunakan untuk mengontrol prioritas operasi).
. foo () {/ * code */} (1) // Ini setara dengan yang berikut, sebuah deklarasi fungsi mengikuti ekspresi yang tidak memiliki hubungan sama sekali: fungsi foo () {/ * kode */} (1);Jalankan Ekspresi Fungsi Segera (IIFE)
Untungnya, mudah untuk memperbaiki kesalahan tata bahasa. Metode yang paling populer dan paling diterima adalah membungkus deklarasi fungsi dalam tanda kurung untuk memberi tahu parser untuk mengekspresikan ekspresi fungsi, karena dalam JavaScript, tanda kurung tidak dapat berisi deklarasi. Karena itu, ketika tanda kurung menemukan kata kunci fungsi untuk membungkus fungsi, ia tahu untuk menguraikannya sebagai ekspresi fungsi alih -alih deklarasi fungsi. Perhatikan pemahaman bahwa tanda kurung di sini berbeda dari tanda kurung di atas ketika mereka menghadapi fungsi, yaitu,
Ketika tanda kurung muncul di akhir fungsi anonim dan ingin memanggil fungsi, akan default untuk memperlakukan fungsi sebagai deklarasi fungsi.
Saat membungkus fungsi dalam tanda kurung, itu akan mengurai fungsi sebagai ekspresi secara default, daripada mendeklarasikan fungsi.
// Kedua pola dapat digunakan untuk memanggil ekspresi fungsi segera, menggunakan eksekusi fungsi untuk membuat variabel pribadi (function () {/ * code */} ()); // Crockford merekomendasikan yang ini, ekspresi dalam braket mewakili fungsi (function () {/ * code */}) ();//tetapi yang ini bekerja dengan baik, dengan baik, {/ * code */} (); operators is to disambiguate// between function expressions and function declarations, they can be// omitted when the parser already expects an expression (but please see the// "important note" below).var i = function(){return 10;}();true && function(){/*code*/}();0,function(){}();//If you don't care about the return value, or make your code as readable as Mungkin, Anda dapat menyimpan byte dengan mengambil operator unary di depan fungsi Anda! function () {/ * code */} (); ~ function () {/ * code */} (); - function () {/ * code */} ();+function () {/ * code */} (); // Berikut ini variasi lain, dari @kuvos - saya tidak yakin kinerja // implikasi, jika ada, menggunakan @kuvos - saya tidak yakin kinerja // implikasi, jika ada, menggunakan @kuvos - saya tidak yakin tentang kinerja // implikasi, jika ada, menggunakan @kuvos - saya tidak yakin tentang kinerja // implikasi, jika ada, menggunakan @kuvos - saya tidak yakin tentang kinerja/ http://twitter.com/kuvos/status/18209252090847232new function () {/ * Code */} Fungsi baru () {/ * kode */} () // hanya membutuhkan parens jika meneruskan argumen () {/ *Catatan penting tentang tanda kurung
Dalam beberapa kasus, tidak perlu untuk mengelilingi ekspresi fungsi ketika kurung ambigu tambahan ada di sekitar ekspresi fungsi (karena tanda kurung sudah dinyatakan sebagai ekspresi), tetapi ini masih merupakan ide yang baik ketika tanda kurung digunakan untuk memanggil ekspresi fungsi.
Kurung seperti itu menunjukkan bahwa ekspresi fungsi akan segera dipanggil dan bahwa variabel akan menyimpan hasil fungsi, bukan fungsi itu sendiri. Ketika ini adalah ekspresi fungsi yang sangat panjang, ini menghemat waktu daripada orang yang membaca kode Anda tanpa harus menggulir ke bagian bawah halaman untuk melihat apakah fungsi tersebut dipanggil.
Sebagai aturan, ketika Anda menulis kode yang jelas dan jelas, perlu untuk mencegah JavaScript dari melempar kesalahan, dan juga perlu untuk mencegah pengembang lain melemparkan kesalahan kepada Anda!
Simpan status penutupan
Sama seperti ketika suatu fungsi dipanggil dengan nama mereka, parameter dilewatkan, dan ketika ekspresi fungsi dipanggil segera, parameter dilewatkan. Ekspresi fungsi yang disebut segera dapat digunakan untuk mengunci nilai dan secara efektif menyimpan keadaan pada saat ini, karena fungsi apa pun yang didefinisikan dalam suatu fungsi dapat menggunakan parameter dan variabel yang dilewati oleh fungsi luar (hubungan ini disebut penutupan).
// Mungkin tidak berfungsi seperti yang Anda pikirkan, karena nilai `Saya tidak pernah terkunci. // Sebaliknya, ketika setiap tautan diklik (loop telah dieksekusi dengan baik), oleh karena itu jumlah total semua elemen akan muncul, // karena ini adalah nilai sebenarnya dari `i` saat ini. var elems = document.getElementsByTagName('a');for(var i = 0;i < elems.length; i++ ) { elems[i].addEventListener('click',function(e){ e.preventDefault(); alert('I am link #' + i) },false);}// And rewrite like below, it's OK, because in IIFE, the `i` value is terkunci di `lockedinindex`. // Ketika loop dieksekusi, meskipun nilai numerik dari nilai `i` adalah jumlah dari semua elemen, setiap kali ekspresi fungsi disebut, nilai` lockedinindex` di IIFE adalah nilai yang diteruskan ke sana oleh `i`, jadi ketika tautan diklik, nilai yang benar muncul. var elems = document.getElementsByTagName('a');for(var i = 0;i < elems.length;i++) { (function(lockedInIndex){ elems[i].addEventListener('click',function(e){ e.preventDefault(); alert('I am link #' + lockedInIndex); },false) })(i);}//You Dapat juga menggunakan IIFE seperti berikut ini, hanya menggunakan tanda kurung untuk memasukkan fungsi pemrosesan klik, dan tidak termasuk seluruh `AddEventListener`. //No matter which way, both examples can be locked with IIFE, but I found that the previous example is more readable var elems = document.getElementsByTagName( 'a' );for ( var i = 0; i < elems.length; i++ ) { elems[ i ].addEventListener( 'click', (function( lockedInIndex ){ return function(e){ e.preventDefault(); alert( 'Saya Link #' + LockedInIndex);}; }Ingatlah bahwa dalam dua contoh terakhir ini, LockedInIndex dapat mengakses I tanpa masalah, tetapi menggunakan pengidentifikasi bernama yang berbeda sebagai parameter fungsi dapat membuat konsep lebih mudah untuk ditafsirkan.
Salah satu keunggulan paling signifikan dari menjalankan fungsi segera adalah bahwa meskipun tidak disebutkan namanya atau anonim, ekspresi fungsi dapat dipanggil segera tanpa menggunakan pengidentifikasi, dan penutupan dapat digunakan tanpa kontaminasi variabel saat ini.
Apa masalah dengan fungsi anonim yang dieksekusi sendiri ("fungsi anonim yang dieksekusi sendiri")?
Anda telah melihatnya disebutkan beberapa kali, tetapi masih belum begitu jelas dijelaskan, saya sarankan mengubah istilah menjadi "ekspresi fungsi yang diinformasikan segera", atau, iife, jika Anda suka singkatan.
Apa ekspresi fungsi yang dihasilkan segera? Itu membuat ekspresi fungsi yang dipanggil segera. Ini seperti ekspresi fungsi yang membuat Anda menelepon.
Saya pikir anggota komunitas JavaScript harus dapat menerima istilah, segera melakukan ekspresi fungsi dan IIFE dalam artikel atau pernyataan mereka, karena saya merasa lebih mudah untuk memahami konsep tersebut, dan istilah "fungsi anonim yang dieksekusi sendiri" benar-benar tidak cukup akurat.
// Berikut ini adalah fungsi yang dieksekusi sendiri, yang secara rekursif menyebut fungsinya sendiri foo () {foo ();}; // Ini adalah fungsi anonim yang dieksekusi sendiri. Karena tidak memiliki pengidentifikasi, ia harus menggunakan properti `argumen.callee` untuk menyebutnya sendiri var foo = function () {arguments.callee ();}; // Ini dapat dianggap sebagai fungsi anonim yang dieksekusi sendiri, tetapi foo yang di bawah ini, tetapi beberapa orang, beberapa orang, tetapi foo () {foo,` foo, `foo,` foo, `foo {foo () {foo () {foo () {foo); Fungsi 'seperti ini, bahkan jika itu tidak dieksekusi sendiri karena tidak memanggil dirinya sendiri. Kemudian, itu baru saja dipanggil segera. (function () {/*code*/} ()); // Menambahkan pengidentifikasi ke ekspresi fungsi (yaitu, membuat fungsi bernama) akan sangat membantu untuk debugging kami. Setelah dinamai, fungsi tidak akan lagi anonim. (function foo () {/ * code */} ()); // iifes juga dapat dieksekusi dengan sendirinya, meskipun mungkin itu bukan pola yang paling berguna (fungsi () {arguments.callee ();} ()) (fungsi foo () {foo ();} ()) // satu hal terakhir yang perlu diperhatikan: ini akan menyebabkan kesalahan. Mengagumkan, ya? (Function foo () {foo ();} ());Semoga contoh di atas akan memberi Anda pemahaman yang lebih jelas tentang istilah yang dieksekusi sendiri yang agak menyesatkan karena tidak mengeksekusi fungsinya sendiri, meskipun fungsinya telah dieksekusi. Demikian pula, tidak perlu menunjukkan fungsi anonim, karena segera ekspresi fungsi dapat berupa fungsi bernama atau fungsi anonim.
Terakhir: Mode Modul
Ketika saya menyebut ekspresi fungsi, jika saya mengingatkan diri sendiri tentang pola modul setidaknya sekali, saya kemungkinan besar akan mengabaikannya. Jika Anda tidak memiliki pola modul dalam JavaScript, itu sangat mirip dengan contoh saya di bawah ini, tetapi nilai pengembalian menggunakan objek alih -alih fungsi.
var counter = (function () {var i = 0; return {get: function () {return i;}, set: function (val) {i = val;}, increment: function () {return ++ i;}}} ()); counter.get (); // 0 counter.set (3); counter.increment (); // 4 counter.increment (); // 5 conuter.i; // tidak terdefinisi (`i` bukan properti dari objek yang dikembalikan) i; // ReferencerError: Saya tidak didefinisikan (hanya ada di dalam penutupan)Metode mode modul tidak hanya cukup kuat tetapi juga sederhana. Dengan kode yang sangat sedikit, Anda dapat secara efektif menggunakan penamaan yang terkait dengan metode dan atribut. Dalam suatu objek, mengatur semua kode modul meminimalkan polusi variabel global dan menciptakan penggunaan variabel.