เราสามารถละเว้นวงเล็บเมื่อสร้างวัตถุโดยใช้ตัวดำเนินการ“ ใหม่” ได้หรือไม่?


229

ฉันเคยเห็นวัตถุที่ถูกสร้างขึ้นด้วยวิธีนี้:

const obj = new Foo;

แต่ฉันคิดว่าวงเล็บไม่ได้เป็นตัวเลือกเมื่อสร้างวัตถุ:

const obj = new Foo();

วิธีการสร้างวัตถุในอดีตนั้นใช้ได้และถูกกำหนดในมาตรฐาน ECMAScript หรือไม่? มีความแตกต่างระหว่างวิธีการสร้างวัตถุในอดีตหรือในภายหลังหรือไม่? เป็นที่ต้องการมากกว่าหนึ่งอื่น ๆ ?



อ้างอิง Updated: ECMAScript 2017 §12.3.3 Opertator
RobG

TL: DR: ระวังที่new a.b()แตกต่างจากnew a().b()ในกรณีก่อนหน้านี้a.bมีการเข้าถึงครั้งแรกในขณะที่ในกรณีหลังใหม่aจะถูกสร้างขึ้นก่อน
แอนดรู

คำตอบ:


238

การอ้างถึงเดวิดฟลานาแกน1 :

ในกรณีพิเศษสำหรับnewโอเปอเรเตอร์เท่านั้น JavaScript ทำให้ไวยากรณ์ง่ายขึ้นโดยอนุญาตให้ละเว้นวงเล็บหากไม่มีอาร์กิวเมนต์ในการเรียกใช้ฟังก์ชัน นี่คือตัวอย่างการใช้newโอเปอเรเตอร์:

o = new Object;  // Optional parenthesis omitted here
d = new Date();  

...

โดยส่วนตัวแล้วฉันใช้วงเล็บเสมอแม้ว่านวกรรมิกจะไม่มีข้อโต้แย้ง

นอกจากนี้JSLintอาจทำร้ายความรู้สึกของคุณหากคุณไม่ใส่วงเล็บ รายงานMissing '()' invoking a constructorและดูเหมือนจะไม่มีตัวเลือกสำหรับเครื่องมือในการทนต่อการละเว้นวงเล็บ


1 David Flanagan: JavaScript the Definitive Guide: 4th Edition (หน้า 75)


6
ทำไม JSLint สนับสนุนให้ใช้วงเล็บ?
Randomblue

11
ฉันคิดว่ามันถือว่ามีความสอดคล้องกันมากกว่านี้
Daniel Vassallo

12
ฉันคิดว่ามันน่าสนใจที่จะเห็นว่าผู้พัฒนาจาวาสคริปต์หลายคนใช้วงเล็บเพราะ "เครื่องมือ (JSLint) บอกให้พวกเขาทำ" โดยเฉพาะอย่างยิ่งเมื่อพิจารณาจากตัวอย่างบนdeveloper.mozilla.org/en-US/docs/Web/JavaScript/Guide / …จาก "คนที่ประดิษฐ์ภาษา <expletive>" ไม่ใช้วงเล็บใด ๆ บนnew Classสำหรับตัวสร้างแบบไม่มีพารามิเตอร์ หากยังไม่สามารถสะกด 'ดื้อดึง' ผมไม่ทราบว่าสิ่งที่ไม่ ...
แอ๊

52
@ack มันจะแปลกที่จะไม่เห็นนักประดิษฐ์ของภาษาแสดงคุณลักษณะบางอย่างของภาษาของพวกเขา (ในกรณีนี้ตัวเลือกที่จะไม่ใส่วงเล็บในตัวสร้าง) หากพวกเขาไม่ได้เพิ่มคุณสมบัตินี้เราจะไม่ถามว่าควรจะใช้คุณลักษณะนี้ตั้งแต่แรกหรือไม่ เหตุผลการปฏิบัติสำหรับการไม่ได้ใช้มันอย่างนี้จะไม่เทียบเท่ากับnew Object.func() new Object().func()การรวมวงเล็บไว้เสมอความเป็นไปได้ในการทำผิดพลาดจะถูกกำจัด
nmclean

2
(new Object).func()หากคุณต้องการที่จะกำจัดความเป็นไปได้ของการทำผิดพลาดที่คุณควรใช้ที่ แต่ฉันคิดว่าการใช้วงเล็บพิเศษและเครื่องหมายเท่ากับพิเศษอย่างเช่นเมื่อ==เทียบกับ===ข้ออ้างที่ไม่ดีสำหรับการไม่เรียนรู้ภาษาของคุณ
Jean Vincent

78

มีความแตกต่างระหว่างสอง:

  • new Date().toString() ทำงานได้อย่างสมบูรณ์และส่งกลับวันที่ปัจจุบัน
  • new Date.toString()พ่น " TypeError: Date.toString ไม่ใช่ตัวสร้าง "

มันเกิดขึ้นเพราะnew Date()และnew Dateมีความสำคัญแตกต่างกัน ตามMDNส่วนหนึ่งของตารางสำคัญของโอเปอเรเตอร์ JavaScript ที่เราสนใจมีลักษณะดังนี้:

╔════════════╦═════════════════════════════╦═══════════════╦═════════════╗
 Precedence         Operator type         Associativity   Operators  
╠════════════╬═════════════════════════════╬═══════════════╬═════════════╣
     18      Member Access                left-to-right   .        
             Computed Member Access       left-to-right    [  ]    
             new (with argument list)     n/a            new  (  ) 
╠════════════╬═════════════════════════════╬═══════════════╬═════════════╣
     17      Function Call                left-to-right   (  )     
             new (without argument list)  right-to-left  new        
╚════════════╩═════════════════════════════╩═══════════════╩═════════════╝

จากตารางนี้มีดังนี้:

  1. new Foo() มีความสำคัญสูงกว่า new Foo

    new Foo()มีลำดับความสำคัญเท่ากันกับ.โอเปอเรเตอร์

    new Fooมีลำดับความสำคัญต่ำกว่า.ผู้ประกอบการหนึ่งระดับ

    new Date().toString() ทำงานได้อย่างสมบูรณ์เพราะมันประเมินว่าเป็น (new Date()).toString()

    new Date.toString()พ่น " TypeError: Date.toString ไม่ใช่ตัวสร้าง " เนื่องจาก.มีลำดับความสำคัญสูงกว่าnew Date(และสูงกว่า "การเรียกใช้ฟังก์ชัน") และการแสดงออกจะประเมินว่า(new (Date.toString))()

    ตรรกะเดียวกันสามารถนำไปใช้กับ… [ … ]ผู้ประกอบการ

  2. new Fooมีการเชื่อมโยงจากขวาไปซ้ายและสำหรับnew Foo()"การเชื่อมโยง" ไม่สามารถใช้ได้ ฉันคิดว่าในทางปฏิบัติมันไม่ได้สร้างความแตกต่างเลย สำหรับข้อมูลเพิ่มเติมดูคำถาม SO นี้


เป็นที่ต้องการมากกว่าหนึ่งอื่น ๆ ?

เมื่อรู้ทุกอย่างแล้วสามารถสันนิษฐานได้ว่าnew Foo()เป็นที่ต้องการ


4
ในที่สุดคนที่ตอบคำถามและชี้ให้เห็นถึงความแตกต่างที่ลึกซึ้ง!
gcampbell

ว้าวขอบคุณ. ทำให้ฉันนึกถึงภาษาอื่นen.wikipedia.org/wiki/Brainfuck
John Henckel

ดีอธิบายข้อเสนอว่าเหตุผลที่ควรเป็นที่ต้องการมากกว่าnew Foo() new Fooคำตอบที่ดีที่สุด
CodeLama

คำตอบที่ยอดเยี่ยมตารางนำหน้าและคำอธิบายประกอบทำให้ชัดเจนอย่างสมบูรณ์ ดีใจที่คุณอธิบายวิธีการที่ทำให้มัน alright การเขียนเช่นเดียวกับnew Object().something() (new Object()).something()
LittleTiger

1
คำอธิบายที่ดี แต่ฉันไม่เห็นด้วยกับข้อสรุปและยังคิดว่าการใช้วงเล็บไม่เป็นไรถ้าคุณรู้ภาษาของคุณ BTW ถ้าคุณไม่ทราบว่ามีความสำคัญ JS คุณยังสามารถใช้การนับตัวอักษรเดียวกันและชัดเจนกว่า(new Date).toString() new Date().toString
Jean Vincent

14

ฉันไม่คิดว่าจะมีความแตกต่างใด ๆ เมื่อคุณใช้โอเปอเรเตอร์ "ใหม่" ระวังเรื่องนิสัยให้ดีเพราะรหัสสองบรรทัดนี้ไม่เหมือนกัน:

var someVar = myFunc; // this assigns the function myFunc to someVar
var someOtherVar = myFunc(); // this executes myFunc and assigns the returned value to someOtherVar

1
ยิ่งมีปัญหามากขึ้นถ้าคุณไม่ใช้อัฒภาค ;-)
RobG

13

หากคุณไม่มีข้อโต้แย้งที่จะผ่านวงเล็บเป็นตัวเลือก การละเว้นพวกมันเป็นเพียงน้ำตาลทราย


8
ฉันจะบอกว่าวากยสัมพันธ์เกลือ แต่ ymmv
Thomas Eding

ฉันจะบอกว่าเพิ่มพวกเขาหากพวกเขาไม่จำเป็นต้องมีน้ำตาลประโยค :)
Cozzbie

8

https://people.mozilla.org/~jorendorff/es6-draft.html#sec-new-operator-runtime-semantics-evaluation

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

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

new Array.length // fails because Array.length is the number 1, not a constructor
new Array().length // 0

มันถูกกำหนดอย่างดีใน ES5 และ ES3 ด้วย - "ส่งคืนผลลัพธ์ของการเรียกเมธอดภายใน [[Construct]] บน Constructor โดยไม่มีอาร์กิวเมนต์ (นั่นคือรายการอาร์กิวเมนต์ว่าง)"
Benjamin Gruenbaum

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