Bagaimana cara kerja sintaksis JavaScript / jQuery ini: (fungsi (jendela, tidak terdefinisi) {}) (jendela)?

153

Pernahkah Anda melihat di bawah kap di kode sumber jQuery 1.4 dan perhatikan bagaimana itu dienkapsulasi dengan cara berikut:

(function( window, undefined ) {

  //All the JQuery code here 
  ...

})(window);

Saya telah membaca sebuah artikel tentang JavaScript Namespacing dan satu lagi yang disebut " Sepasang Penting Orangtua ", jadi saya tahu beberapa tentang apa yang terjadi di sini.

Tapi saya belum pernah melihat sintaks khusus ini sebelumnya. Apa yang undefineddilakukan di sana? Dan mengapa windowharus dilewati dan kemudian muncul di akhir lagi?

dkinzer
sumber
3
Saya ingin menambahkan bahwa Paul Irish berbicara tentang ini dalam video ini: paulirish.com/2010/10-things-i-learned-from-the-jquery-source
Mottie
@Bergi Anda menandai pertanyaan saya sebagai duplikat dari yang lain tetapi saya mengajukan pertanyaan hampir setahun sebelum duplikat. Para pemain harus sebaliknya.
dkinzer
@dzerzer: Ini bukan tentang ditanya sebelumnya, ini tentang jawaban berkualitas tinggi. Memang, dalam hal ini leher dan leher, tetapi saya menemukan jawaban CMS menjadi yang terbaik. Datang ke chat.stackoverflow.com/rooms/17 untuk membahas ini,
Bergi

Jawaban:

161

Undefined adalah variabel normal dan dapat diubah dengan undefined = "new value";. Jadi jQuery membuat variabel "tidak terdefinisi" lokal yang BENAR-BENAR tidak terdefinisi.

Variabel jendela dibuat lokal untuk alasan kinerja. Karena ketika JavaScript mencari variabel, pertama-tama ia akan melalui variabel lokal hingga ia menemukan nama variabel. Ketika tidak ditemukan, JavaScript melewati ruang lingkup berikutnya dll. Hingga filter melalui variabel global. Jadi, jika variabel jendela dibuat lokal, JavaScript dapat melihatnya lebih cepat. Informasi lebih lanjut: Percepat JavaScript Anda - Nicholas C. Zakas

Vincent
sumber
1
Terima kasih! itu video yang sangat informatif. Saya pikir Anda perlu mengedit respons Anda untuk mengatakan "variabel jendela dibuat lokal". Saya pikir ini adalah jawaban terbaik. Saya menghitung dan menemukan bahwa objek jendela dipanggil setidaknya 17 kali dalam kode sumber JQuery. Jadi harus ada efek signifikan dalam memindahkan jendela ke Lingkup Lokal.
dkinzer
@Dinzer Oh ya, kau benar, tentu saja itu dibuat lokal. Senang saya bisa membantu Anda =)
Vincent
1
Menurut tes yang diperbarui ini jsperf.com/short-scope/5 melewati 'jendela' sebenarnya lebih lambat, ketika datang untuk mendapatkan konstanta jendela melalui jQuery, dan khususnya buruk untuk Safari. Jadi sementara 'tidak terdefinisi' memiliki use case, saya tidak cukup yakin bahwa 'window' sangat berguna dalam semua kasus.
hexalys
1
Ini juga memiliki efek positif untuk memperkecil kode. Jadi setiap penggunaan "window" dan "undefined" dapat diganti dengan nama yang lebih pendek oleh minifyer.
TLindig
2
@ lukas.pukenis Saat Anda membuat perpustakaan yang digunakan di jutaan situs web, sebaiknya jangan membuat asumsi tentang apa yang akan dilakukan orang gila.
JLRishe
54

Tidak terdefinisi

Dengan mendeklarasikan undefined sebagai argumen tetapi tidak pernah memberikan nilai padanya memastikan bahwa itu selalu tidak terdefinisi, karena itu hanya variabel dalam lingkup global yang dapat ditimpa. Ini membuat a === undefinedalternatif yang aman typeof a == 'undefined', yang menghemat beberapa karakter. Ini juga membuat kode lebih ramah-minifier, seperti undefineddapat disingkat menjadi u, misalnya, menyimpan beberapa karakter lagi.

Jendela

Lulus windowsebagai argumen menyimpan salinan di lingkup lokal, yang memengaruhi kinerja: http://jsperf.com/short-scope . Semua akses ke windowsekarang harus melakukan perjalanan satu tingkat kurang dari rantai ruang lingkup. Seperti halnya undefined, salinan lokal lagi memungkinkan untuk minifikasi yang lebih agresif.


Sidenote:

Meskipun ini mungkin bukan maksud dari pengembang jQuery, lewat windowmemungkinkan perpustakaan menjadi lebih mudah diintegrasikan dalam lingkungan Javascript sisi-server, misalnya node.js - di mana tidak ada windowobjek global . Dalam situasi seperti itu, hanya satu baris yang perlu diubah untuk mengganti windowobjek dengan yang lain. Dalam kasus jQuery, windowobjek tiruan dapat dibuat dan diteruskan untuk tujuan pengikisan HTML (perpustakaan seperti jsdom dapat melakukan ini).

David Tang
sumber
2
+1 untuk menyebutkan Minification, seandainya saya bisa +2 untuk jsperf - Versi minified adalah (function(a,b){})(window);- adan bjauh lebih pendek daripada windowdan undefined:)
gnarf
+1 Saya pernah mendengar tentang rantai lingkup sebelumnya, tetapi lupa istilahnya. Terima kasih!
alex
Ini juga merupakan alternatif untuk menggunakan void (0) yang dimaksudkan untuk alasan yang sama: void (0) adalah cara yang aman untuk mendapatkan nilai yang tidak ditentukan.
glmxndr
"Apa yang Anda katakan" mengacu pada pertanyaan lain ini: stackoverflow.com/questions/4945265/…
gnarf
@gnarf, terima kasih atas pemberitahuan bahwa pertanyaannya telah digabungkan. Saya akan mengedit jawaban saya agar lebih masuk akal;)
David Tang
16

Yang lain sudah menjelaskan undefined. undefinedseperti variabel global yang dapat didefinisikan ulang untuk nilai apa pun. Teknik ini untuk mencegah semua cek yang tidak ditentukan dari melanggar jika seseorang menulis katakan, di undefined = 10suatu tempat. Argumen yang tidak pernah disahkan dijamin nyata undefinedterlepas dari nilai variabel undefined .

Alasan untuk lewat jendela dapat diilustrasikan dengan contoh berikut.

(function() {
   console.log(window);
   ...
   ...
   ...
   var window = 10;
})();

Apa yang dicatat oleh konsol konsol? Nilai windowobjeknya benar? Salah! 10? Salah! Ini log undefined. Penerjemah Javascript (atau kompiler JIT) menulis ulang dengan cara ini -

(function() {
   var window; //and every other var in this function

   console.log(window);
   ...
   ...
   ...
   window = 10;

})();

Namun, jika Anda mendapatkan windowvariabel sebagai argumen, tidak ada var dan karenanya tidak ada kejutan.

Saya tidak tahu apakah jQuery melakukannya, tetapi jika Anda mendefinisikan ulang windowvariabel lokal di mana saja dalam fungsi Anda untuk alasan apa pun, itu ide yang baik untuk meminjamnya dari lingkup global.

Chetan S
sumber
6

window dilewatkan seperti itu kalau-kalau seseorang memutuskan untuk mendefinisikan kembali objek jendela di IE, saya berasumsi sama untuk undefined , dalam kasus itu ditugaskan kembali dalam beberapa cara nanti.

Bagian atas windowdalam skrip itu hanya menamai argumen "jendela", argumen yang lebih lokal bahwa windowreferensi global dan apa kode di dalam penutupan ini akan digunakan. Yang windowpada akhirnya sebenarnya menentukan apa yang harus dilewati untuk argumen pertama, dalam hal ini arti saat ini window... harapannya adalah Anda belum mengacaukan windowsebelum itu terjadi.

Ini mungkin lebih mudah untuk memikirkan dengan menunjukkan kasus yang khas yang paling digunakan dalam jQuery, Plugin .noConflict()penanganan, sehingga untuk sebagian besar kode Anda masih bisa menggunakan $, bahkan jika itu berarti sesuatu yang lain dari jQueryluar lingkup ini:

(function($) {
  //inside here, $ == jQuery, it was passed as the first argument
})(jQuery);
Nick Craver
sumber
Terima kasih. Ini sangat masuk akal. Padahal, saya pikir jawabannya adalah kombinasi dari ini dan apa yang dikatakan @Zakas.
dkinzer
@Dinzer Aku takut aku bukan C. Zakas;)
Vincent
@Vincent, maaf tentang itu :-)
dkinzer
5

Diuji dengan iterasi 1000000. Pelokalan semacam ini tidak mempengaruhi kinerja. Bahkan satu milidetik dalam iterasi 1000000. Ini sama sekali tidak berguna.

Semra
sumber
2
Belum lagi, closure membawa semua variabel bersamaan dengan instance fungsi, jadi jika Anda memiliki 100 byte dalam closure, dan 1000 instance yang 100kb lebih banyak memori.
Akash Kava
@AkashKava dan @Semra, saya tidak berpikir tes ini benar-benar menjelaskan mengapa Anda akan melewati jendela dalam situasi dunia nyata. Keuntungan kinerja hanya akan terlihat ketika Anda memiliki penutupan bersarang yang mengakses variabel itu lebih jauh di sepanjang rantai lingkup. jelas jika fungsi Anda hanya selangkah lagi pada rantai lingkup dari variabel, Anda tidak akan melihat banyak keuntungan. tetapi jika itu waaaay di sana dan perlu untuk mendapatkan windowAnda mungkin melihat beberapa keuntungan dengan salinan lokal.
gabereal
@gabereal mesin JavaScript baru menyelesaikan penutupan pada saat kompilasi itu sendiri, tidak ada keuntungan kinerja pada penutupan saat runtime. Saya ragu ada peningkatan kinerja yang dapat diukur, jika Anda sangat percaya diri, Anda dapat membuat contoh jsperf untuk menunjukkan kinerja. Satu-satunya kinerja yang kami dapatkan adalah dengan mengisolasi penutupan yang menghasilkan minifikasi yang lebih baik yang mengurangi ukuran dan kinerja dicapai dengan pengunduhan dan penguraian skrip yang lebih cepat.
Akash Kava
@AkashKava apa yang membuatmu berpikir aku sangat percaya diri? Saya hanya mengatakan "mungkin melihat beberapa keuntungan" dan "saya tidak berpikir". Saya bisa membuat tes, tetapi saya lebih suka tidak karena saya tidak pernah menggunakan pola ini. dapatkah Anda menjelaskan apa yang Anda maksud dengan "menyelesaikan penutupan pada saat kompilasi itu sendiri"? sebagai lawan dari alternatif apa? bagaimana mesin Javascript melakukan sesuatu yang berbeda sebelumnya?
gabereal
Telah menemukan kode di mana jendela digulung di akhir tetapi tidak dimasukkan dalam parameter fungsi, apa artinya? itu memastikan bahwa parameter mengikuti objek lingkup global asli bahkan jika tidak ada parameter jendela yang saya asumsikan?
Webwoman