Können wir beim Erstellen eines Objekts mit dem Operator "new" Klammern weglassen?

229

Ich habe gesehen, wie Objekte auf diese Weise erstellt wurden:

const obj = new Foo;

Aber ich dachte, dass die Klammern beim Erstellen eines Objekts nicht optional sind:

const obj = new Foo();

Ist die bisherige Methode zum Erstellen von Objekten im ECMAScript-Standard gültig und definiert? Gibt es Unterschiede zwischen der früheren Art der Objekterstellung und der späteren? Wird einer dem anderen vorgezogen?

Behrang Saeedzadeh
quelle
Aktualisierte Referenz: ECMAScript 2017 §12.3.3 Der neue Operator .
RobG
TL; DR: Beachten Sie, dass dies new a.b()anders new a().b()ist, als im ersteren Fall a.bzuerst zugegriffen wird, während im letzteren Fall zuerst ein neues aerstellt wird.
Andrew

Antworten:

238

Zitat David Flanagan 1 :

Als Sonderfall newvereinfacht JavaScript nur für den Operator die Grammatik, indem die Klammer weggelassen wird, wenn der Funktionsaufruf keine Argumente enthält. Hier einige Beispiele mit dem newOperator:

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

...

Persönlich verwende ich immer die Klammer, auch wenn der Konstruktor keine Argumente akzeptiert.

Darüber hinaus kann JSLint Ihre Gefühle verletzen, wenn Sie die Klammern weglassen. Es wird berichtet Missing '()' invoking a constructor, und es scheint keine Option für das Tool zu geben, um das Auslassen von Klammern zu tolerieren.


1 David Flanagan: JavaScript the Definitive Guide: 4. Ausgabe (Seite 75)

Daniel Vassallo
quelle
6
Warum empfiehlt JSLint die Verwendung von Klammern?
Randomblue
11
Ich denke, es wird nur als konsistenter angesehen.
Daniel Vassallo
12
Ich finde es interessant zu sehen, dass viele JavaScript-Entwickler Klammern verwenden, nur weil "das Tool (JSLint) sie dazu aufgefordert hat", insbesondere angesichts der Beispiele auf developer.mozilla.org/en-US/docs/Web/JavaScript/Guide /… , Von "den Leuten, die die <expletive> Sprache erfunden haben", verwenden keine Klammern new Classfür parameterlose Konstruktoren. Wenn dies nicht "eigensinnig" bedeutet, weiß ich nicht, was ...
ack
52
@ack Nun wäre es ungerade nicht die Sprache der Erfinder zeigen bestimmte Merkmale ihrer Sprache zu sehen (in diesem Fall die Option wegzulassen Klammern auf Konstrukteuren). Wenn sie die Funktion nicht hinzugefügt hätten, würden wir nicht fragen, ob sie überhaupt verwendet werden sollte. Ein praktischer Grund für die Nichtverwendung ist folgender: new Object.func()ist NICHT gleichbedeutend mit new Object().func(). Indem immer Klammern eingeschlossen werden, wird die Möglichkeit, diesen Fehler zu machen, ausgeschlossen.
nmclean
2
Wenn Sie die Möglichkeit eines Fehlers ausschließen möchten, sollten Sie verwenden (new Object).func(). Aber ich halte die Verwendung zusätzlicher Klammern und zusätzlicher Gleichheitszeichen wie in ==vs für ===eine schlechte Entschuldigung , wenn Sie Ihre Sprache nicht lernen.
Jean Vincent
78

Es gibt Unterschiede zwischen den beiden:

  • new Date().toString() funktioniert einwandfrei und gibt das aktuelle Datum zurück
  • new Date.toString()löst " TypeError: Date.toString ist kein Konstruktor "

Es passiert weil new Date()und new Datehaben unterschiedliche Vorrang. Laut MDN sieht der Teil der JavaScript-Operator-Prioritätstabelle, an dem wir interessiert sind, folgendermaßen aus:

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

Aus dieser Tabelle folgt:

  1. new Foo() hat eine höhere Priorität als new Foo

    new Foo()hat die gleiche Priorität wie der .Operator

    new Foohat eine Stufe niedrigere Priorität als der .Bediener

    new Date().toString() funktioniert perfekt, weil es als bewertet (new Date()).toString()

    new Date.toString()löst " TypeError: Date.toString ist kein Konstruktor " aus, da es .eine höhere Priorität als new Date(und höher als "Funktionsaufruf") hat und der Ausdruck als ausgewertet wird(new (Date.toString))()

    Die gleiche Logik kann auf den … [ … ]Bediener angewendet werden.

  2. new Foohat Assoziativität von rechts nach links und für new Foo()"Assoziativität" ist nicht anwendbar. Ich denke in der Praxis macht es keinen Unterschied. Weitere Informationen finden Sie in dieser SO-Frage


Wird einer dem anderen vorgezogen?

Wenn man das alles weiß, kann man davon ausgehen, dass dies new Foo()bevorzugt wird.

Traxium
quelle
4
Endlich jemand, der die Frage tatsächlich beantwortet und auf den subtilen Unterschied hinweist!
Gcampbell
Wow, danke. erinnert mich an eine andere Sprache en.wikipedia.org/wiki/Brainfuck
John Henckel
Gut erklärt, bietet genau den Grund, warum new Foo()bevorzugt werden sollte new Foo. Die bisher beste Antwort.
CodeLama
Eine großartige Antwort, die Rangfolge und die dazugehörige Erklärung machen es völlig klar. Ich bin froh, dass du erklärst, wie es in Ordnung ist, new Object().something()so gut wie zu schreiben (new Object()).something().
LittleTiger
1
Tolle Erklärung, aber ich bin mit der Schlussfolgerung nicht einverstanden und denke immer noch, dass es in Ordnung ist, keine Klammern zu verwenden, wenn Sie Ihre Sprache kennen. Übrigens, wenn Sie die JS-Priorität nicht kennen, können Sie auch (new Date).toString()dieselbe Zeichenanzahl und expliziter als verwenden new Date().toString.
Jean Vincent
14

Ich glaube nicht, dass es einen Unterschied gibt, wenn Sie den "neuen" Operator verwenden. Seien Sie vorsichtig, wenn Sie sich an diese Gewohnheit gewöhnen, da diese beiden Codezeilen NICHT identisch sind:

var someVar = myFunc; // this assigns the function myFunc to someVar
var someOtherVar = myFunc(); // this executes myFunc and assigns the returned value to someOtherVar
jbabey
quelle
1
Noch problematischer, wenn Sie die Gewohnheit haben, Semikolons wegzulassen. ;-)
RobG
13

Wenn Sie keine Argumente übergeben müssen, sind die Klammern optional. Sie wegzulassen ist nur syntaktischer Zucker.

Josh
quelle
8
Ich würde syntaktisches Salz sagen, aber ymmv.
Thomas Eding
Ich würde sagen, sie hinzuzufügen, wenn sie nicht benötigt werden, ist syntaktischer Zucker. :)
Cozzbie
8

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

Hier ist der Teil der ES6-Spezifikation, der definiert, wie die beiden Varianten funktionieren. Die Variante ohne Klammern übergibt eine leere Argumentliste.

Interessanterweise haben die beiden Formen unterschiedliche grammatikalische Bedeutungen. Dies wird angezeigt, wenn Sie versuchen, auf ein Mitglied des Ergebnisses zuzugreifen.

new Array.length // fails because Array.length is the number 1, not a constructor
new Array().length // 0
Gast
quelle
Es ist auch in ES5 und ES3 gut definiert: "Gibt das Ergebnis des Aufrufs der internen Methode [[Construct]] für den Konstruktor zurück und gibt keine Argumente an (dh eine leere Liste von Argumenten)."
Benjamin Gruenbaum
3

Es gibt keinen Unterschied zwischen den beiden.

Ivan
quelle