ฟังก์ชั่นจาวาสคริปต์ชั้นนำปัง! วากยสัมพันธ์


204

ฉันเคยเห็นรูปแบบนี้ในห้องสมุดไม่กี่ตอนนี้และฉันสงสัยว่าผลประโยชน์คืออะไร (โปรดทราบว่าฉันทราบดีถึงการปิดและสิ่งที่รหัสกำลังทำอยู่ฉันกังวลเฉพาะความแตกต่างด้านโครงสร้างประโยค)

!function(){
  // do stuff
}();

เป็นทางเลือกให้กับคนทั่วไปมากขึ้น

(function(){
  // do stuff
})();

สำหรับการเรียกใช้ฟังก์ชันที่ไม่ระบุชื่อ

ฉันสงสัยบางสิ่ง ก่อนอื่นสิ่งที่ทำให้ตัวอย่างบนสุดใช้งานได้จริงคืออะไร เพราะเหตุใดจึงจำเป็นในการทำให้คำสั่งนี้ถูกต้องทางไวยากรณ์? ฉันยังบอกว่าใช้+งานได้และฉันแน่ใจว่าบางคนอื่นแทน!

ประการที่สองประโยชน์คืออะไร? ทั้งหมดที่ฉันสามารถบอกได้คือมันช่วยตัวละครตัวเดียว แต่ฉันไม่สามารถจินตนาการได้ว่ามันเป็นประโยชน์อย่างมากที่จะดึงดูดผู้ใช้งานจำนวนมาก มีประโยชน์อื่นอีกไหมที่ฉันขาดหายไป?

ความแตกต่างอื่น ๆ ที่ฉันเห็นคือค่าส่งคืนของฟังก์ชันการเรียกใช้ตนเอง แต่ในตัวอย่างทั้งสองนี้เราไม่สนใจค่าส่งคืนของฟังก์ชันเนื่องจากใช้เพื่อสร้างการปิดเท่านั้น ดังนั้นบางคนสามารถบอกฉันได้ว่าทำไมอาจใช้ไวยากรณ์แรก


เฟรมเวิร์กต้องการบันทึกอักขระจำนวนมากเท่าที่จะทำได้ซึ่งตัวปรับขนาดเล็กไม่สามารถปรับให้เหมาะสม
alex

ประโยชน์แรกมาถึงหัวของฉันคือคุณสามารถสร้าง sandbox เช่น (function ($) {... }) (jQuery);
JS Taylor

2
ตัวอย่างทั้งสองบรรลุเป้าหมายนี้ เป็นที่กล่าวถึงผมเข้าใจในสิ่งที่ฟังก์ชั่นเหล่านี้จะทำผมสนใจมากขึ้นในความแตกต่างของการสร้างประโยค
แบรด

8
ฉันพูดถึงความต้องการนีนี่เป็นสิ่งที่น่ารักในแบบที่ซับซ้อนกว่าทีมก่อนหน้านี้ "โอ้พ่อแม่พวกนอกศาสนาพวกเรามี !!"
Jared Farrish

2
ฉันไม่เคยรู้เรื่องนี้เลย ฉันชอบที่!มันเน้นว่ามันจะถูกประหารชีวิต
cdmckay

คำตอบ:


97

เป็นการดีที่คุณควรจะสามารถทำสิ่งนี้ทั้งหมดเพียงแค่:

function(){
  // do stuff
}(); 

นั่นหมายถึงประกาศฟังก์ชั่นที่ไม่ระบุชื่อและดำเนินการ แต่นั่นจะไม่ทำงานเนื่องจากคุณสมบัติเฉพาะของไวยากรณ์ JS

ดังนั้นรูปแบบที่สั้นที่สุดของการบรรลุเป้าหมายนี้คือการใช้นิพจน์บางอย่างเช่น UnaryExpression (และ CallExpression):

!function(){
  // do stuff
}(); 

หรือเพื่อความสนุก:

-function(){
  // do stuff
}(); 

หรือ:

+function(){
  // do stuff
}(); 

หรือแม้กระทั่ง:

~function(){
  // do stuff
  return 0;
}( );

3
ดังนั้นความอดทนจึงเป็นประโยชน์อย่างเดียวเท่านั้น ?
แบรด

12
ฉันได้เพิ่มตัวเลือกทั้งหมดด้านบนลงในการทดสอบประสิทธิภาพที่: jsperf.com/bang-function
Shaz

5
ฉันจะบอกว่า ความเร็วไม่ได้มีความสำคัญในกรณีเช่นนี้เพราะมันทำงานครั้งเดียว
c-smile

1
จากความอยากรู้อยากเห็นฉันสังเกตเห็นว่าไม่มีปังและปังทำหน้าที่ได้เร็วที่สุด แต่บางแห่งในวิวัฒนาการของ Chrome อย่างน้อยก็กลายเป็นปังเร็วกว่าไม่ปัง (อาจไม่มีนัยสำคัญทางสถิติ แต่ ... ) มีเหตุผลทำไม? ฉันไม่ค่อยรู้อะไรเกี่ยวกับรหัสภายใต้ประทุน แต่คิดว่ามันน่าสนใจทีเดียว
jmk2142

โอเปอเรเตอร์สองตัวของคุณสามารถตีความได้ว่าเป็นเลขฐานสองหากเซมิโคลอนหายไป ตัวแรกและตัวสุดท้ายเป็นตัวอย่างที่ปลอดภัยที่สุด

73

ใน Javascript บรรทัดที่ขึ้นต้นด้วยfunctionคาดว่าจะเป็นคำสั่งฟังก์ชั่นและควรจะมีลักษณะ

function doSomething() {
}

ฟังก์ชั่นที่เรียกตนเอง

function(){
  // do stuff
}();

ไม่พอดีกับรูปแบบที่ (และจะทำให้เกิดการผิดพลาดทางไวยากรณ์ที่วงเล็บเปิดครั้งแรกเพราะไม่มีชื่อฟังก์ชัน) ดังนั้นวงเล็บจะใช้ในการวาดภาพฟังก์ชั่นที่ไม่ระบุชื่อการแสดงออก

(function(){
  // do stuff
})();

แต่สิ่งใดก็ตามที่สร้างนิพจน์ (ตรงข้ามกับคำสั่งฟังก์ชั่น) จะทำเช่นนั้น !นั้น กำลังบอกล่ามว่านี่ไม่ใช่คำสั่งฟังก์ชั่น นอกเหนือจากนั้นลำดับความสำคัญของโอเปอเรเตอร์จะกำหนดว่าจะเรียกใช้ฟังก์ชันก่อนการปฏิเสธ

ฉันไม่ได้ตระหนักถึงอนุสัญญานี้ แต่ถ้าเป็นเรื่องปกติมันอาจช่วยให้อ่านได้ สิ่งที่ฉันหมายถึงคือไม่มีใครอ่าน!functionที่ด้านบนของบล็อกขนาดใหญ่ของรหัสจะคาดหวัง-ภาวนาด้วยตนเอง, (functionวิธีการที่เราจะมีการปรับอากาศอยู่แล้วที่จะคาดหวังเช่นเดียวกันเมื่อเราเห็น ยกเว้นว่าเราจะสูญเสียวงเล็บที่น่ารำคาญเหล่านั้น ฉันคาดหวังว่านั่นเป็นเหตุผลตรงข้ามกับการประหยัดความเร็วหรือจำนวนตัวละคร


"บรรทัดที่เริ่มต้นด้วยฟังก์ชันคาดว่า ... " นี้จะค่อนข้างคลุมเครือ สิ่งนี้:var foo = {CR/LF here} function bar() {}
c-smile

45

นอกจากสิ่งที่พูดไปแล้วไวยากรณ์กับ! มีประโยชน์ถ้าคุณเขียน javascript ที่ไม่มีเครื่องหมายอัฒภาค:

var i = 1
!function(){
  console.log('ham')
}()

i = 2
(function(){
  console.log('cheese')
})()

ตัวอย่างแรกส่งออก 'ham' ตามที่คาดไว้ แต่ข้อที่สองจะเกิดข้อผิดพลาดเนื่องจากคำสั่ง i = 2 ไม่ได้ถูกยกเลิกเนื่องจากวงเล็บต่อไปนี้

นอกจากนี้ในไฟล์จาวาสคริปต์ที่ต่อกันคุณไม่ต้องกังวลหากโค้ดก่อนหน้านี้ไม่มีเครื่องหมายอัฒภาค ดังนั้นไม่จำเป็นต้องมีสามัญ (function () {}) (); เพื่อให้แน่ใจว่าคุณจะไม่แตก

ฉันรู้ว่าคำตอบของฉันสายไปแล้ว แต่ฉันคิดว่ามันยังไม่ได้กล่าวถึง :)


6
+1 สำหรับเคล็ดลับและความทรงจำ ... ไม่มีใครชอบเขียน javascript ที่ไม่มีเครื่องหมายอัฒภาคในวันนี้
Pablo Grisafi

4
Twitter Bootstrap คือ JS ทั้งหมดที่ไม่มีเครื่องหมายอัฒภาคและมันค่อนข้างใหม่ ... (ขออภัยถ้าความคิดเห็นข้างต้นนั้นเป็นการเสียดสีในธรรมชาติและฉันพลาดมันไป)
brandwaffle

2
สิ่งนี้ไม่เป็นความจริง ลองพิจารณาตัวอย่างต่อไปนี้:! function () {console.log ("ham");} ()! function () {console.log ("ชีส")} (); คุณได้รับข้อผิดพลาด: "SyntaxError: โทเค็นที่ไม่คาดคิด!"
วิ่งหนัก

ใช่คุณถูก. มีข้อผิดพลาดหากคุณใส่คำเหล่านั้นในบรรทัดเดียวกัน ฉันไม่ได้คิดอย่างนั้น แต่ไม่มีสคริปต์การเรียงต่อกัน / lib ที่ฉันใช้จนถึงตอนนี้ และเมื่อคุณย่อขนาดและต่อท้ายอัฒภาคไฟล์ของคุณจะถูกเพิ่มเข้าไป ดังนั้นโดยสุจริตฉันไม่สามารถจินตนาการถึงกรณีที่คุณพบปัญหา ในทางตรงกันข้ามมันเป็น 02:00 ที่นี่และผมอาจจะไม่รู้ :)
Smoe

6

สำหรับสิ่งหนึ่ง jsPerf แสดงให้เห็นว่าการใช้!(ของ UnaryExpression) นั้นเร็วกว่า บางครั้งพวกเขาก็ออกมาเท่ากัน แต่เมื่อพวกเขาไม่ฉันไม่ได้เห็นชัยชนะหนึ่งที่ไม่มีการกระแทกมากกว่าคนอื่นมากเกินไป: http://jsperf.com/bang-function

สิ่งนี้ได้รับการทดสอบบน Ubuntu ล่าสุดด้วย Chrome ที่เก่าแก่ที่สุด (ต่อพูด .. ) รุ่น 8 ดังนั้นผลลัพธ์อาจแตกต่างกันไป

แก้ไข: สิ่งที่เกี่ยวกับสิ่งที่บ้าชอบdelete?

delete function() {
   alert("Hi!"); 
}();

หรือvoid?

void function() {
   alert("Hi!"); 
}();

! ที่น่าสนใจ ฉันได้ประมาณ 50/50 แม้ว่าใน Safari ความเร็วฉลาดและไม่กระแทกแน่นอนมันเป็นของตัวเอง ฉันสงสัยว่ามีทฤษฎีเกี่ยวกับความแตกต่างของความเร็วที่เป็นไปได้หรือไม่?
แบรด

@brad ต้องเป็นสิ่งที่จะทำอย่างไรกับซาฟารีถ้าคุณมองไปที่การทดสอบส่วนใหญ่ของเบราว์เซอร์อื่น ๆ !ดูเหมือนจะนิยม แต่ฉันก็อยากจะหาทฤษฎีเกี่ยวกับเรื่องนี้เช่นกัน :)
21411 Shaz

3
ฉันชอบโมฆะเพราะมันพูดอย่างชัดเจนว่า "ฉันไม่สนใจค่าตอบแทน" และเพราะมันทำให้ฉันนึกถึงการประชุมในภาษาอื่น ๆ
code_monk

4

ดังที่คุณเห็นที่นี่วิธีที่ดีที่สุดในการใช้วิธีเรียกตัวเองใน javascript คือ:

(function(){}()); -> 76,827,475 ops/sec

!function(){}();  -> 69,699,155 ops/sec

(function(){})(); -> 69,564,370 ops/sec

1
ฉันสงสัยว่าประสิทธิภาพเป็นเหตุผลที่ใช้รูปแบบเดียวหรืออีกรูปแบบหนึ่ง ที่ 70M ops / วินาทีการประกาศการปิดจะเร็วกว่าพันเท่าของรหัสข้างใน
Eloims

3

ดังนั้นด้วยการปฏิเสธ "!" และโอเปอเรเตอร์ unary อื่น ๆ ทั้งหมดเช่น +, -, ~, ลบ, เป็นโมฆะมีการบอกจำนวนมากเพียงเพื่อสรุป:

!function(){
  alert("Hi!");
}(); 

หรือ

void function(){
  alert("Hi!");
}();

หรือ

delete function(){
  alert("Hi!");
}();

และอีกสองสามกรณีที่มีตัวดำเนินการไบนารีเพื่อความสนุก :)

1 > function() {
   alert("Hi!"); 
}();

หรือ

1 * function() {
   alert("Hi!"); 
}();

หรือ

1 >>> function() {
   alert("Hi!"); 
}();

หรือแม้กระทั่ง

1 == function() {
   alert("Hi!"); 
}();

ออกจากแบบประกอบการสำหรับคนอื่น ๆ :)


12
0?0:function() { alert("Hi!"); }();
daniel1426

บิงโกแดน! เรามีผู้ประกอบการรายหนึ่ง;)
Arman McHitarian
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.