เครื่องหมายอัศเจรีย์ทำหน้าที่อะไรก่อนฟังก์ชั่น


1242
!function () {}();

4
@Dykam มีประโยชน์อธิบายไว้ในคำตอบนี้: stackoverflow.com/questions/5422585/…
hectorct



7
@befzz ดีกว่าที่จะพูดถึงเรื่องนี้ในฐานะที่เป็นฟังก์ชั่นการแสดงออกรื้อฟื้นทันทีเป็นบทความที่อธิบายในภายหลัง ( "ตัวเองดำเนินการ" หมายถึงการเรียกซ้ำ)
ซัค Esposito

คำตอบ:


2117

ไวยากรณ์ JavaScript 101 นี่คือการประกาศฟังก์ชัน :

function foo() {}

หมายเหตุว่าไม่มีอัฒภาค: นี้เป็นเพียงฟังก์ชั่นการประกาศ คุณจะต้องมีการร้องขอfoo()ให้เรียกใช้ฟังก์ชัน

ตอนนี้เมื่อเราเพิ่มเครื่องหมายอัศเจรีย์ที่ดูเหมือนไม่มีอันตราย: !function foo() {}มันจะเปิดลงในการแสดงออก ตอนนี้มันเป็นการแสดงออกของฟังก์ชั่น

!เพียงอย่างเดียวไม่ได้เรียกใช้ฟังก์ชั่นของหลักสูตร แต่ตอนนี้เราสามารถใส่()ที่สิ้นสุด: !function foo() {}()ซึ่งมีความสำคัญสูงกว่า!และทันทีที่เรียกฟังก์ชัน

ดังนั้นสิ่งที่ผู้เขียนทำคือการบันทึกไบต์ต่อฟังก์ชั่นการแสดงออก วิธีการเขียนที่อ่านง่ายกว่านี้ก็คือ:

(function(){})();

สุดท้าย!ทำให้การแสดงออกกลับเป็นจริง เพราะนี่คือการเริ่มต้นทุกการกลับมา IIFE undefinedซึ่งทำให้เรามีซึ่งเป็น!undefined trueไม่มีประโยชน์อย่างยิ่ง


229
+1 นี่คือคำตอบที่ดีที่สุดที่นี่และเศร้า upvoted แทบจะไม่ เห็นได้ชัดว่า!คืนบูลีนเราทุกคนรู้ แต่จุดที่ดีที่คุณทำคือมันยังแปลงคำสั่งประกาศฟังก์ชั่นการแสดงออกของฟังก์ชั่นเพื่อให้สามารถเรียกใช้ฟังก์ชั่นได้ทันทีโดยไม่ต้องใส่มันในวงเล็บ ไม่ชัดเจนและเจตนาของ coder ชัดเจน
gilly3

64
+1 นี่เป็นคำตอบเดียวที่ตอบว่าทำไมคุณถึงต้องการทำเช่นนี้และทำไมจึงเห็นว่ามันใช้งานมากกว่าการปฏิเสธผลตอบแทนที่ได้รับดูเหมือนจะรับประกัน ผู้ประกอบการเอก! (เช่น ~, - และ +) disambiguates จากการประกาศฟังก์ชั่นและอนุญาตให้ parens ที่ส่วนท้าย () เรียกใช้ฟังก์ชันในตำแหน่ง สิ่งนี้มักจะทำเพื่อสร้างขอบเขต / เนมสเปซในพื้นที่สำหรับตัวแปรเมื่อเขียนโค้ดแบบแยกส่วน
Tom Auger

65
ประโยชน์อีกอย่างก็คือ! ทำให้เกิดการแทรกเซมิโคลอนดังนั้นจึงเป็นไปไม่ได้ที่รุ่นนี้จะต่อกับไฟล์ที่ไม่ได้ลงท้ายด้วย; หากคุณมีรูปแบบ () มันจะพิจารณาว่าเป็นการเรียกใช้ฟังก์ชันของสิ่งที่กำหนดไว้ในไฟล์ก่อนหน้า เคล็ดลับหมวกกับเพื่อนร่วมงานของฉัน
Jure Triglav

5
@Carnix var foo =ทำลายคำแถลงการณ์ / การแสดงออกซึ่งคุณสามารถเขียนvar foo = function(bar){}("baz");ฯลฯ ได้
Neil

6
โดยปกติจะทำโดยสคริปต์ minification / uglification ซึ่งทุก ๆ ไบต์มีค่า
Dima Slivin

367

ฟังก์ชั่น:

function () {}

ส่งคืนอะไร (หรือไม่ได้กำหนด)

บางครั้งเราต้องการเรียกฟังก์ชันที่ถูกต้องในขณะที่เราสร้างมันขึ้นมา คุณอาจถูกทดลองให้ทำสิ่งนี้:

function () {}()

SyntaxErrorแต่มันจะส่งผลให้

การใช้!โอเปอเรเตอร์ก่อนที่ฟังก์ชั่นจะทำให้เป็นนิพจน์ดังนั้นเราจึงสามารถเรียกมันได้ว่า:

!function () {}()

นอกจากนี้ยังจะกลับตรงข้ามบูลของค่าตอบแทนของฟังก์ชั่นในกรณีนี้trueเพราะเป็น!undefined trueหากคุณต้องการให้ค่าที่ส่งคืนจริงเป็นผลมาจากการโทรลองทำวิธีนี้:

(function () {})()

28
ใครสามารถต้องการสิ่งนี้ได้บ้าง
Andrey

13
นี่เป็นคำตอบเดียวที่อธิบายกรณีในคำถามไชโย!
Andrey

14
ตัวอย่างรหัสที่สองของคุณไม่ใช่ JavaScript ที่ถูกต้อง วัตถุประสงค์ของการที่!จะเปลี่ยนการประกาศฟังก์ชั่นในการแสดงออกฟังก์ชั่นนั่นคือทั้งหมดที่
Skilldrick

8
@Andrey ทวิตเตอร์ bootstrap ใช้สิ่งนี้ในทุกไฟล์ปลั๊กอิน javascript (jQuery) การเพิ่มความคิดเห็นนี้ในกรณีที่คนอื่นอาจมีคำถามเดียวกัน
Anmol Saraf

2
d3.js ยังใช้ซินแทก!functionซ์
Kristian

65

มีจุดที่ดีสำหรับการใช้!สำหรับการเรียกใช้ฟังก์ชันที่ทำเครื่องหมายไว้ในคู่มือ JavaScript ของ airbnb

โดยทั่วไปแนวคิดสำหรับการใช้เทคนิคนี้ในไฟล์แยก (โมดูล aka) ซึ่งต่อมาได้รับการต่อกัน ข้อแม้ที่นี่คือไฟล์ที่ควรจะมีการต่อกันโดยเครื่องมือที่วางไฟล์ใหม่ที่บรรทัดใหม่ (ซึ่งเป็นพฤติกรรมทั่วไปสำหรับเครื่องมือ concat ส่วนใหญ่) ในกรณีดังกล่าวการใช้!จะช่วยหลีกเลี่ยงข้อผิดพลาดหากโมดูลที่ต่อกันก่อนหน้านี้พลาดเซมิโคลอนต่อท้ายและยังให้ความยืดหยุ่นในการวางไว้ในลำดับใด ๆ โดยไม่ต้องกังวล

!function abc(){}();
!function bca(){}();

จะทำงานเหมือนกัน

!function abc(){}();
(function bca(){})();

แต่ช่วยตัวละครตัวหนึ่งและดูดีขึ้นโดยพลการ

และโดยวิธีการใด ๆ ของ+, -, ~, voidผู้ประกอบการมีผลเช่นเดียวกันในแง่ของการเรียกฟังก์ชั่นเพื่อตรวจสอบว่าถ้าคุณมีการใช้สิ่งที่จะกลับมาจากฟังก์ชั่นที่พวกเขาจะทำหน้าที่แตกต่างกัน

abcval = !function abc(){return true;}() // abcval equals false
bcaval = +function bca(){return true;}() // bcaval equals 1
zyxval = -function zyx(){return true;}() // zyxval equals -1
xyzval = ~function xyz(){return true;}() // your guess?

แต่ถ้าคุณใช้รูปแบบ IIFE สำหรับหนึ่งไฟล์แยกรหัสโมดูลหนึ่งและใช้เครื่องมือ concat สำหรับการเพิ่มประสิทธิภาพ (ซึ่งทำให้งานหนึ่งไฟล์หนึ่งบรรทัด) จากนั้นสร้าง

!function abc(/*no returns*/) {}()
+function bca() {/*no returns*/}()

จะทำการรหัสที่ปลอดภัยเช่นเดียวกับตัวอย่างรหัสแรก

อันนี้จะทำให้เกิดข้อผิดพลาดทำให้ JavaScript ASI ไม่สามารถทำงานได้

!function abc(/*no returns*/) {}()
(function bca() {/*no returns*/})()

หนึ่งหมายเหตุเกี่ยวกับตัวดำเนินการ unary พวกเขาจะทำงานคล้ายกัน แต่เฉพาะในกรณีที่พวกเขาไม่ได้ใช้ในโมดูลแรก ดังนั้นมันจึงไม่ปลอดภัยหากคุณไม่สามารถควบคุมลำดับการรวมทั้งหมดได้

งานนี้:

!function abc(/*no returns*/) {}()
^function bca() {/*no returns*/}()

นี่ไม่ใช่:

^function abc(/*no returns*/) {}()
!function bca() {/*no returns*/}()

3
ที่จริงแล้วสัญลักษณ์อื่น ๆ เหล่านั้นไม่มีผลเหมือนกัน ใช่พวกเขาอนุญาตให้คุณเรียกใช้ฟังก์ชันตามที่อธิบายไว้ แต่ไม่เหมือนกัน ลองพิจารณา: var foo =! function (bar) {console.debug (bar); }("ค้างคาว"); ไม่ว่าคุณจะใส่สัญลักษณ์อะไรไว้ข้างหน้าคุณจะได้ "ค้างคาว" ในคอนโซลของคุณ ตอนนี้เพิ่ม console.debug ("foo:", foo); - คุณได้รับผลลัพธ์ที่แตกต่างกันมากโดยขึ้นอยู่กับสัญลักษณ์ที่คุณใช้ ! บังคับให้ส่งคืนค่าที่ไม่ต้องการเสมอไป ฉันชอบไวยากรณ์ ({}) () เพื่อความชัดเจนและความถูกต้อง
Carnix

29

มันกลับมาว่าคำสั่งสามารถประเมินเป็นเท็จ เช่น:

!false      // true
!true       // false
!isValid()  // is not valid

คุณสามารถใช้สองครั้งเพื่อรวมค่าเป็นบูลีน:

!!1    // true
!!0    // false

ดังนั้นเพื่อตอบคำถามของคุณโดยตรงมากขึ้น:

var myVar = !function(){ return false; }();  // myVar contains true

แก้ไข:มันมีผลข้างเคียงของการเปลี่ยนการประกาศฟังก์ชั่นการแสดงออกของฟังก์ชั่น เช่นรหัสต่อไปนี้ไม่ถูกต้องเพราะถูกตีความว่าเป็นการประกาศฟังก์ชันที่ไม่มีตัวระบุที่ต้องการ(หรือชื่อฟังก์ชัน ):

function () { return false; }();  // syntax error

6
เพื่อความชัดเจนสำหรับผู้อ่านที่อาจต้องการใช้การมอบหมายด้วยฟังก์ชั่นที่ถูกเรียกใช้ในทันทีโค้ดตัวอย่างของคุณvar myVar = !function(){ return false; }()อาจละเว้นสิ่งที่!คล้ายกันvar myVar = function(){ return false; }()และฟังก์ชันจะทำงานอย่างถูกต้องและค่าส่งคืนจะไม่ถูกแตะต้อง
Mark Fox

1
เพื่อความชัดเจนคุณสามารถใช้หนึ่งครั้งเพื่อบีบบังคับเพราะบูลีนเพราะมันไม่ใช่ตัวดำเนินการเชิงตรรกะ ! 0 = true และ! 1 = false สำหรับวัตถุประสงค์ minification JavaScript คุณต้องการที่จะแทนที่trueด้วย!0และมีfalse !1มันประหยัด 2 หรือ 3 ตัวอักษร
Triynko

9

เป็นเพียงการบันทึกข้อมูลจำนวนหนึ่งเมื่อเราทำการลดขนาดจาวาสคริปต์

พิจารณาฟังก์ชั่นที่ไม่ระบุชื่อด้านล่าง

function (){}

ในการทำให้ฟังก์ชั่นข้างต้นเป็นฟังก์ชั่นการเรียกตนเองเรามักจะเปลี่ยนรหัสข้างต้นเป็น

(function (){}())

ตอนนี้เราเพิ่มอักขระพิเศษสองตัว(,)นอกเหนือจากการเพิ่ม()ที่ส่วนท้ายของฟังก์ชันที่จำเป็นในการเรียกใช้ฟังก์ชัน ในกระบวนการของการลดขนาดเรามักมุ่งเน้นที่จะลดขนาดไฟล์ ดังนั้นเราจึงสามารถเขียนฟังก์ชันข้างต้นเป็น

!function (){}()

ทั้งสองเป็นฟังก์ชั่นที่เรียกใช้ตัวเองและเราบันทึกไบต์เช่นกัน แทนที่จะใช้ 2 ตัวอักษร(,)เราแค่ใช้ตัวละครตัวเดียว!


1
สิ่งนี้มีประโยชน์เพราะบ่อยครั้งที่คุณจะเห็นสิ่งนี้ในรูปย่อขนาดย่อ
สำหรับชื่อ

5

! เป็นโอเปอเรเตอร์NOTแบบลอจิคัลเป็นโอเปอเรเตอร์บูลีนที่จะแปลงค่ากลับเป็นตรงกันข้าม

แม้ว่าคุณสามารถข้ามวงเล็บของฟังก์ชันที่เรียกใช้โดยใช้BANG (!) ก่อนหน้าที่จะยังคงกลับค่าซึ่งอาจไม่ใช่สิ่งที่คุณต้องการ เช่นในกรณีของ IEFE มันจะกลับมาไม่ได้กำหนดซึ่งเมื่อกลับกลายเป็นบูลีนจริง

ให้ใช้วงเล็บปิดและ BANG ( ! ) แทนหากต้องการ

// I'm going to leave the closing () in all examples as invoking the function with just ! and () takes away from what's happening.

(function(){ return false; }());
=> false

!(function(){ return false; }());
=> true

!!(function(){ return false; }());
=> false

!!!(function(){ return false; }());
=> true

ผู้ประกอบการอื่นที่ทำงาน ...

+(function(){ return false; }());
=> 0

-(function(){ return false; }());
=> -0

~(function(){ return false; }());
=> -1

ผู้ประกอบการรวม ...

+!(function(){ return false; }());
=> 1

-!(function(){ return false; }());
=> -1

!+(function(){ return false; }());
=> true

!-(function(){ return false; }());
=> true

~!(function(){ return false; }());
=> -2

~!!(function(){ return false; }());
=> -1

+~(function(){ return false; }());
+> -1

5

เครื่องหมายอัศเจรีย์ทำให้ฟังก์ชันใด ๆ ส่งคืนบูลีนเสมอ
ค่าสุดท้ายคือการปฏิเสธค่าที่ส่งคืนโดยฟังก์ชัน

!function bool() { return false; }() // true
!function bool() { return true; }() // false

ถนัด!ในตัวอย่างข้างต้นจะเป็นSyntaxError

function bool() { return true; }() // SyntaxError

อย่างไรก็ตามวิธีที่ดีกว่าในการบรรลุเป้าหมายนี้คือ:

(function bool() { return true; })() // true

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

3

อีกวิธีหนึ่งในการเขียน IIFE (นิพจน์ฟังก์ชันที่เรียกใช้ทันที)

วิธีอื่นในการเขียน -

(function( args ) {})()

เหมือนกับ

!function ( args ) {}();

มันไม่เหมือนกันทุกประการ แบบฟอร์มที่ 2 คัดค้านผลของการเรียกใช้ฟังก์ชัน (แล้วโยนทิ้งเนื่องจากไม่มีการกำหนดค่า) ฉันต้องการ(function (args) {...})()ไวยากรณ์ที่ชัดเจนยิ่งขึ้นและปล่อยให้!functionรูปแบบนั้นเป็นเครื่องมือ minification และ obfuscation
โทเบียส

-1

! จะลบล้าง (ตรงข้าม) สิ่งที่คุณคาดหวังว่าจะเป็นผลคือถ้าคุณมี

var boy = true;
undefined
boy
true
!boy
false

เมื่อคุณเรียกboyผลของคุณจะมีtrueแต่ช่วงเวลาที่คุณเพิ่ม!เมื่อโทรboyคือผลของคุณจะ!boy falseซึ่งในคำอื่น ๆ ที่คุณหมายถึงNotBoyแต่เวลานี้มันเป็นพื้นผลบูลทั้งหรือtruefalse

นั่นคือสิ่งเดียวกับที่เกิดขึ้นกับ!function () {}();การแสดงออกทำงานเฉพาะfunction () {}();ประสงค์ธงคุณมีข้อผิดพลาด แต่เพิ่ม!ตรงหน้าของคุณfunction () {}();แสดงออกทำให้มันตรงข้ามของซึ่งควรจะกลับมาที่คุณfunction () {}(); trueตัวอย่างสามารถดูได้ด้านล่าง:

function () {}();
SyntaxError: function statement requires a name
!function () {}();
true
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.