ES6 เรียกใช้ฟังก์ชันลูกศรทันที


149

เหตุใดจึงทำงานในNode.jsคอนโซล (ทดสอบใน 4.1.1 และ 5.3.0) แต่ไม่ทำงานในเบราว์เซอร์ (ทดสอบใน Chrome) Okการป้องกันรหัสนี้ควรสร้างและเรียกใช้ฟังก์ชั่นที่ไม่ระบุชื่อที่บันทึก

() => {
  console.log('Ok');
}()

นอกจากนี้ในขณะที่การทำงานข้างต้นในโหนดนี้ไม่ทำงาน:

n => {
  console.log('Ok');
}()

หรือสิ่งนี้:

(n) => {
  console.log('Ok');
}()

มีอะไรแปลก ๆ คือเมื่อเพิ่มพารามิเตอร์แล้วมันจะส่ง a ไปSyntaxErrorที่ส่วนที่เรียกใช้ทันที


8
คำถามที่ดี. ทั้งสองพารามิเตอร์แบบใช้งานได้กับ Babel
CodingIntrigue

2
ไม่ได้(n => { console.log("Ok"); })();ผลทำงานได้หรือไม่
CodingIntrigue

ใช่(n => { console.log("Ok"); })()ทำงานได้แม้ในคอนโซล Chrome dev
XCS

ดังนั้น 3 ปีต่อมาคำตอบคืออะไร? แน่นอนหนึ่งใน 3 คำตอบที่ควรได้รับการยอมรับ!
joedotnot

@joednot ฉันไม่ได้รับคำตอบที่ชัดเจนส่วนใหญ่เป็นการใช้งานที่แปลกใน Node.js ดูเหมือนว่าในเวอร์ชันล่าสุดของNode.jsเวอร์ชันแรกจะไม่ทำงานอีกต่อไป
XCS

คำตอบ:


194

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

(() => {
  console.log('Ok');
})()

เทียบเท่ากับIIFE

(function(){
   console.log('Ok')
})();

และเหตุผลที่เป็นไปได้ว่าทำไมสิ่งนี้ถึงใช้ได้ใน Node.js แต่ไม่ใช่ใน chrome เพราะ parser ตีความว่ามันเป็นฟังก์ชั่นการดำเนินการด้วยตนเองเช่นนี้

function() { console.log('hello'); }();

ทำงานได้ดีในNode.jsนี่คือการแสดงออกของฟังก์ชั่นและโครเมี่ยมและ Firefox และเบราว์เซอร์ส่วนใหญ่ตีความมันด้วยวิธีนี้ คุณต้องเรียกใช้ด้วยตนเอง

วิธีที่ได้รับการยอมรับอย่างกว้างขวางที่สุดในการบอกตัวแยกวิเคราะห์เพื่อคาดหวังว่าการแสดงออกของฟังก์ชั่นเป็นเพียงการห่อใน parens เพราะใน JavaScript parens ไม่สามารถมีคำสั่ง เมื่อมาถึงจุดนี้เมื่อ parser พบคีย์เวิร์ดของฟังก์ชันมันจะรู้ว่ามันแยกเป็นนิพจน์ฟังก์ชันและไม่ใช่การประกาศฟังก์ชัน

เกี่ยวกับรุ่นที่เป็นพารามิเตอร์นี้จะใช้งานได้

((n) => {
  console.log('Ok');
})()

4
ตัวอย่างแรกทำงานNode.jsและบันทึกค่า คำถามของฉันคือทำไมมันทำงาน และทำไมเมื่อฉันเพิ่มพารามิเตอร์
XCS

1
ฉันค่อนข้างคุ้นเคยกับIIFEs และรู้วิธีแก้ไขรหัสของฉัน ฉันแค่อยากรู้ว่าทำไมยกตัวอย่างเช่นฉันIIFEไม่ทำงานเมื่อnเพิ่มพารามิเตอร์ถึงแม้ว่ามันจะทำงานโดยไม่มีพารามิเตอร์ก็ตาม
XCS

3
ฉันไม่ได้ลงคะแนน แต่คำถามคือทำไมรุ่นที่กำหนดพารามิเตอร์ไม่ทำงานใน Node เมื่อคำจำกัดความเดียวกันโดยไม่มีพารามิเตอร์ไม่ได้ถามความแตกต่างระหว่างการใช้งาน Node / Chrome ของฟังก์ชันที่ไม่ระบุชื่อ
CodingIntrigue

1
มันเป็นเรื่องที่รู้ดี แต่ไม่ตอบคำถามดังที่กล่าวมา - ทำไมเวอร์ชันที่มีพารามิเตอร์ไม่ทำงานใน Node เมื่อนิยามเดียวกันที่แน่นอนโดยไม่มีพารามิเตอร์ทำ
jkris

แต่อะไรคือสิ่งที่เทียบเท่ากับfunction(){}()ฟังก์ชั่นลูกศร? ถ้าฉันต้องการให้วัตถุฟังก์ชั่นสัมผัสฟังก์ชั่นการแสดงออกป้องกันนั้น
dabadaba

18

สิ่งเหล่านี้ไม่สามารถใช้งานได้หากไม่มีวงเล็บ

ทำไม?

เพราะตามข้อมูลจำเพาะ:

  1. ArrowFunctionอยู่ในรายการAssignment Expression
  2. LHS ของCallExpressionจะต้องเป็นMemberExpression , SuperCallหรือCallExpression

ดังนั้นArrowFunctionไม่สามารถจะอยู่ใน LHS ของที่CallExpression


สิ่งนี้มีประสิทธิภาพหมายถึงในวิธีการที่=>ควรจะตีความก็คือว่ามันทำงานในประเภทเดียวกันของระดับเป็นผู้ประกอบการที่ได้รับมอบหมาย=, +=ฯลฯ

ความหมาย

  • x => {foo}() ไม่เป็น(x => {foo})()
  • ล่ามพยายามที่จะตีความมันเป็น x => ({foo}())
  • ดังนั้นมันจึงยังคงเป็นSyntaxError
  • ดังนั้นล่ามจึงตัดสินใจว่า(จะต้องมีความผิดและส่งSyntaxError

มีข้อผิดพลาดใน Babel เกี่ยวกับที่นี่เช่นกัน


นั่นเป็นจุดที่ถูกต้อง แต่ถ้าฉันพยายามแทนที่เวอร์ชันแรกที่ใช้งานได้ด้วย: () => ({console.log('Ok')}())มันใช้งานไม่ได้อีกต่อไป ดังนั้นมันจึงไม่ตีความอย่างนั้น
XCS

@Cristy ไม่ใช่ Arrow Function ที่ถูกต้อง คิดว่าคุณกำลังพยายามสร้างวัตถุที่มีตัวอักษร Object (ล้อมรอบด้วยวงเล็บ) และconsole.log(...)ไม่ใช่ชื่อคีย์ที่ถูกต้อง
thefourtheye 4

@ Cristy: ใช่ฉันคิดว่าส่วนการตีความของข้างต้น (บิต "ความหมาย") อาจไม่ถูกต้องนัก แต่ชิ้นส่วนสเปคนั้นเท่าที่ฉันสามารถบอกได้ นอกจากนี้ยังเหมาะกับข้อผิดพลาดที่ฉันได้รับจาก V8: SyntaxError: Unexpected token ((ชี้ไปที่(ใน()ตอนท้ายไม่ใช่(ในconsole.log(...))
TJ Crowder

@TJCrowder คุณพูดถูกฉันจะขีดฆ่าเพราะมันเปลี่ยนข้อความแสดงข้อผิดพลาดและสิ่งที่ฉันพยายามจะพูดนั้นไม่ได้สื่อความหมาย (นั่นเป็น(สาเหตุให้ล่ามเลิกเพราะความพยายามที่จะหาคำแปลที่ถูกต้องและหมดไป "นี่ต้องผิดแล้ว") ซึ่งอาจผิดเพราะฉันไม่รู้ว่าล่ามเขียนจริง ๆ อย่างไร
พอลเอส.

ฉันสงสัยว่ามันไม่ใช่โทเค็นที่ถูกต้องในตำแหน่งนี้หรือไม่มันจะพยายามแทรกเครื่องหมายโคลอนกึ่งหรือไม่
thefourtheye 4

2

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

> 3 + 2
< 5

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

นี่เป็นหนึ่งในสาเหตุที่รหัส ES6 เปลือยในสคริปต์ทำงานได้ดี แต่ไม่ได้อยู่ในคอนโซล Chrome Dev Tools

ลองดำเนินการนี้ใน Node และคอนโซล Chrome:

{ let a = 3 }

ในโหนดหรือ<script>แท็กมันทำงานได้ดี Uncaught SyntaxError: Unexpected identifierแต่ในคอนโซลจะช่วยให้ นอกจากนี้ยังให้ลิงก์ไปยังแหล่งข้อมูลในรูปแบบVMxxx:1ที่คุณสามารถคลิกเพื่อตรวจสอบแหล่งที่ได้รับการประเมินซึ่งแสดงเป็น:

({ let a = 3 })

แล้วทำไมมันถึงทำเช่นนี้?

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

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

นี่เป็นตัวอย่างที่ดีของบางสิ่งที่คล้ายกันมาก:

https://stackoverflow.com/a/28431346/46588

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

กล่าวโดยย่อ:คอนโซลพยายามจำลองบริบทการดำเนินการทั่วโลกอย่างแม่นยำที่สุด แต่เนื่องจากข้อ จำกัด ของการโต้ตอบกับเอ็นจิ้น v8 และความหมายของ JavaScript บางครั้งก็ยากหรือแก้ไม่ได้


1
นั่นคือทั้งหมดที่ฉันสนใจเกี่ยวกับพารามิเตอร์ แต่มันไม่ทำงานกับชุดพารามิเตอร์
XCS

ตกลงฉันเห็นจุดของคุณ ข้อแตกต่างคือวิธีที่คอนโซล Chrome Dev Tools ใช้รหัสของคุณจริงๆ ฉันจะแก้ไขคำตอบเพื่อสะท้อนสิ่งนี้
Klemen Slavič

0

ฉันถามคำถามเช่นนี้:

@getify ฉันมีคำถามนี้: ในการสร้างรูปแบบ #IIFE เราใช้ parans รอบการประกาศฟังก์ชั่นเพื่อแปลงเป็นนิพจน์ฟังก์ชั่นแล้วเรียกมัน ตอนนี้ในฟังก์ชั่น IIFE ของลูกศรทำไมเราต้องมี parans ด้วย! ฟังก์ชั่นลูกศรไม่ได้แสดงออกมาเป็นค่าเริ่มต้นอยู่แล้ว!

และนี่คือคำตอบของ Kyle Simpson:

ฟังก์ชั่นลูกศรเป็น expr แต่เราต้องการ parens รอบ b / c ของ "โอเปอเรเตอร์สำคัญ" (sorta) ดังนั้น parens สุดท้ายที่จะเรียกใช้ Arrow-IIFE ใช้กับฟังก์ชั่นทั้งหมดและไม่ใช่แค่สัญลักษณ์สุดท้ายของร่างกาย .

x => console.log(x)(4)

VS

(x => console.log(x))(4)

- getify (@getify) 12 มิถุนายน 2563


คำถามของฉันคือทำไมมันใช้กับคอมไพเลอร์บางตัว
XCS

นั่นเป็นเพราะคอมไพเลอร์ที่แตกต่างกันทำงานในรายละเอียดบางอย่างเช่นเบราว์เซอร์ที่แตกต่างกันซึ่งแน่นอนว่ามีคอมไพเลอร์ที่แตกต่างกัน
Ershad Qaderi

คุณถูกต้องพวกเขาทำงานแตกต่างกัน แต่ข้อกำหนด JavaScript เหมือนกันสำหรับพวกเขาทั้งหมด ฉันอยากรู้ว่าอันไหนที่ถูกต้องข้อมูลจำเพาะของ JS พูดเกี่ยวกับกรณีนี้และโดยเฉพาะอย่างยิ่งว่าเป็นไปได้อย่างไรที่มันจะทำงานโดยไม่มีข้อโต้แย้ง แต่ไม่ได้โต้แย้ง ฉันกำลังมองหาคำตอบทางเทคนิคเพิ่มเติม
XCS

console.log(x)(4)ตัวอย่างที่เห็นได้ชัดของคุณสวยในกรณีแรกมันจริงควรจะเรียก
XCS

ฉันแค่เดาที่นี่ แต่ฉันคิดว่ามันสมเหตุสมผลมากที่จะอธิบายอย่างนี้ในนิพจน์ฟังก์ชั่นลูกศรเมื่อเราไม่ใช้พารามิเตอร์เราต้องใช้ parens และนั่นทำให้ชัดเจนสำหรับเครื่องยนต์ที่เป็นลูกศร การแสดงออกของฟังก์ชั่น แต่เมื่อเรามีพารามิเตอร์เดียว parens จะไม่มีกฎเกณฑ์ซึ่งอาจไม่ชัดเจนว่าฟังก์ชั่นนี้และสร้างความสับสนให้กับเครื่องยนต์นั้นเพื่อแก้ไขความสับสนเราต้องใส่ parens คู่หนึ่งรอบการแสดงออกของฟังก์ชั่นทั้งหมด
Ershad Qaderi
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.