È possibile omettere le parentesi quando si crea un oggetto utilizzando il "nuovo" operatore?


229

Ho visto oggetti creati in questo modo:

const obj = new Foo;

Ma ho pensato che le parentesi non sono opzionali durante la creazione di un oggetto:

const obj = new Foo();

Il primo modo di creare oggetti è valido e definito nello standard ECMAScript? Ci sono differenze tra il primo modo di creare oggetti e il successivo? L'uno è preferito all'altro?



Riferimento aggiornato: ECMAScript 2017 §12.3.3 Il nuovo Operatore .
RobG

TL; DR: attenzione che new a.b()è diverso da new a().b()quello in cui, nel primo caso, a.bsi accede per la prima volta, mentre nel secondo caso, aviene creato prima un nuovo .
Andrew,

Risposte:


238

Citando David Flanagan 1 :

Come caso speciale, solo per l' newoperatore, JavaScript semplifica la grammatica consentendo di omettere la parentesi se non ci sono argomenti nella chiamata di funzione. Ecco alcuni esempi usando l' newoperatore:

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

...

Personalmente, utilizzo sempre la parentesi, anche quando il costruttore non accetta argomenti.

Inoltre, JSLint può ferire i tuoi sentimenti se ometti la parentesi. Riferisce Missing '()' invoking a constructore non sembra esserci un'opzione per lo strumento per tollerare l'omissione tra parentesi.


1 David Flanagan: JavaScript the Definitive Guide: 4th Edition (page 75)


6
Perché JSLint incoraggia l'uso della parentesi?
Randomblue,

11
Immagino sia solo considerato più coerente.
Daniel Vassallo,

12
Trovo interessante vedere che molti sviluppatori JavaScript usano le parentesi semplicemente perché "lo strumento (JSLint) ha detto loro di farlo", soprattutto considerando che gli esempi su developer.mozilla.org/en-US/docs/Web/JavaScript/Guide / ... , da "i ragazzi che hanno inventato il linguaggio <expletive>" non usano parentesi new Classper costruttori senza parametri. Se ciò non significa "supponente", non so che cosa ...
ack

52
@ack Beh, sarebbe strano non vedere gli inventori del linguaggio mostrare alcune caratteristiche del loro linguaggio (in questo caso, l'opzione di omettere le parentesi sui costruttori). Se non avessero aggiunto la funzione, non avremmo chiesto se dovesse essere utilizzata in primo luogo. Un motivo pratico per non usarlo è questo: new Object.func()NON è equivalente a new Object().func(). Includendo sempre le parentesi, viene eliminata la possibilità di commettere questo errore.
nmclean

2
Se si desidera eliminare la possibilità di commettere un errore, è necessario utilizzare (new Object).func(). Ma considero l'uso di parentesi extra e segni di uguaglianza extra, come in ==vs ===, una brutta scusa per non imparare la tua lingua.
Jean Vincent,

78

Ci sono differenze tra i due:

  • new Date().toString() funziona perfettamente e restituisce la data corrente
  • new Date.toString()genera " TypeError: Date.toString non è un costruttore "

Succede perché new Date()e new Datehanno una precedenza diversa. Secondo MDN la parte della tabella di precedenza degli operatori JavaScript a cui siamo interessati è simile a:

╔════════════╦═════════════════════════════╦═══════════════╦═════════════╗
 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        
╚════════════╩═════════════════════════════╩═══════════════╩═════════════╝

Da questa tabella segue che:

  1. new Foo() ha una precedenza maggiore di new Foo

    new Foo()ha la stessa precedenza .dell'operatore

    new Fooha un livello di precedenza inferiore rispetto .all'operatore

    new Date().toString() funziona perfettamente perché valuta come (new Date()).toString()

    new Date.toString()genera " TypeError: Date.toString non è un costruttore " perché .ha una precedenza maggiore di new Date(e maggiore di "Chiamata di funzione") e l'espressione viene valutata come(new (Date.toString))()

    La stessa logica può essere applicata … [ … ]all'operatore.

  2. new Fooha associatività da destra a sinistra e per new Foo()"associatività" non è applicabile. Penso che in pratica non faccia alcuna differenza. Per ulteriori informazioni, consultare questa domanda SO


L'uno è preferito all'altro?

Sapendo tutto ciò, si può presumere che new Foo()sia preferito.


4
Infine, qualcuno che effettivamente risponde alla domanda e sottolinea la sottile differenza!
gcampbell

Wow grazie. mi ricorda un'altra lingua en.wikipedia.org/wiki/Brainfuck
John Henckel

Ben spiegato, offre esattamente il motivo per cui new Foo()dovrebbe essere preferito rispetto new Foo. La migliore risposta finora.
CodeLama,

La risposta eccezionale, la tabella delle precedenza e la spiegazione di accompagnamento lo rendono completamente chiaro. Sono contento che spiegare come che rende bene per scrivere new Object().something(), così come (new Object()).something().
LittleTiger

1
Grande spiegazione ma non sono d'accordo con la conclusione e continuo a pensare che non usare la parentesi vada bene se conosci la tua lingua. A proposito, se non conosci la precedenza di JS, puoi anche usare (new Date).toString(), lo stesso numero di caratteri e più esplicito di new Date().toString.
Jean Vincent,

14

Non credo che ci sia alcuna differenza quando si utilizza il "nuovo" operatore. Fai attenzione a prendere questa abitudine, poiché queste due righe di codice NON sono uguali:

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

1
Ancora più problematico se hai l'abitudine di omettere i punti e virgola. ;-)
RobG

13

Se non si hanno argomenti da passare, le parentesi sono opzionali. Ometterli è solo zucchero sintattico.


8
Direi sale sintattico, ma ymmv.
Thomas Eding,

Direi che aggiungerli se non sono necessari è lo zucchero sintattico. :)
Cozzbie,

8

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

Ecco la parte della specifica ES6 che definisce come funzionano le due varianti. La variante senza parentesi passa un elenco di argomenti vuoto.

È interessante notare che le due forme hanno significati grammaticali diversi. Viene visualizzato quando si tenta di accedere a un membro del risultato.

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

È ben definito anche in ES5 ed ES3 - "Restituisce il risultato della chiamata del metodo interno [[Costruisci]] sul costruttore, senza fornire argomenti (ovvero un elenco vuoto di argomenti)."
Benjamin Gruenbaum,

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.