Peut-on omettre les parenthèses lors de la création d'un objet à l'aide de l'opérateur «nouveau»?

229

J'ai vu des objets créés de cette façon:

const obj = new Foo;

Mais je pensais que les parenthèses ne sont pas facultatives lors de la création d'un objet:

const obj = new Foo();

L'ancienne façon de créer des objets est-elle valide et définie dans la norme ECMAScript? Existe-t-il des différences entre l'ancienne façon de créer des objets et la dernière? Est-ce que l'un est préféré à l'autre?

Behrang Saeedzadeh
la source
Référence mise à jour: ECMAScript 2017 §12.3.3 Le nouvel Opertator .
RobG
TL; DR: Méfiez - vous de ce qui new a.b()est différent new a().b()dans la mesure où, dans le premier cas, vous a.baccédez pour la première fois, alors que dans le dernier cas, un nouveau aest créé en premier.
Andrew

Réponses:

238

Citant David Flanagan 1 :

Comme cas spécial, pour l' newopérateur uniquement, JavaScript simplifie la grammaire en permettant d'omettre la parenthèse s'il n'y a pas d'arguments dans l'appel de fonction. Voici quelques exemples d'utilisation de l' newopérateur:

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

...

Personnellement, j'utilise toujours la parenthèse, même lorsque le constructeur ne prend aucun argument.

De plus, JSLint peut blesser vos sentiments si vous omettez la parenthèse. Il signale Missing '()' invoking a constructor, et il ne semble pas y avoir d'option pour que l'outil tolère l'omission de parenthèses.


1 David Flanagan: JavaScript le guide définitif: 4e édition (page 75)

Daniel Vassallo
la source
6
Pourquoi JSLint encourage-t-il l'utilisation de parenthèses?
Randomblue
11
Je suppose que c'est juste considéré comme plus cohérent.
Daniel Vassallo
12
Je trouve intéressant de voir que de nombreux développeurs JavaScript utilisent des parenthèses simplement parce que "l'outil (JSLint) leur a dit de le faire", d'autant plus que les exemples sur developer.mozilla.org/en-US/docs/Web/JavaScript/Guide /… , De "ceux qui ont inventé le langage <explétif>" n'utilisent pas de parenthèses new Classpour les constructeurs sans paramètres. Si cela n'épelle pas «d'opinion», je ne sais pas ce qui fait ...
ack
52
@ack Eh bien, il serait étrange de ne pas voir les inventeurs du langage présenter certaines caractéristiques de leur langage (dans ce cas, l'option d'omettre les parenthèses sur les constructeurs). S'ils n'avaient pas ajouté la fonctionnalité, nous ne demanderions pas si elle devrait être utilisée en premier lieu. Une raison pratique de ne pas l'utiliser est la suivante: new Object.func()n'est PAS équivalent à new Object().func(). En incluant toujours des parenthèses, la possibilité de faire cette erreur est éliminée.
nmclean du
2
Si vous voulez éliminer la possibilité de faire une erreur, vous devez l'utiliser (new Object).func(). Mais je considère que l'utilisation de parenthèses supplémentaires et de signes égaux supplémentaires, comme dans ==vs ===, est une mauvaise excuse pour ne pas apprendre votre langue.
Jean Vincent
78

Il existe des différences entre les deux:

  • new Date().toString() fonctionne parfaitement et renvoie la date actuelle
  • new Date.toString()lance " TypeError: Date.toString n'est pas un constructeur "

Cela arrive parce que new Date()et new Dateont une priorité différente. Selon MDN, la partie du tableau de priorité des opérateurs JavaScript qui nous intéresse ressemble à:

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

Il ressort de ce tableau que:

  1. new Foo() a une priorité plus élevée que new Foo

    new Foo()a la même priorité que l' .opérateur

    new Fooa un niveau de priorité inférieur à celui de l' .opérateur

    new Date().toString() fonctionne parfaitement car il évalue (new Date()).toString()

    new Date.toString()renvoie " TypeError: Date.toString n'est pas un constructeur " car .a une priorité plus élevée que new Date(et supérieure à "Appel de fonction") et l'expression est évaluée comme(new (Date.toString))()

    La même logique peut être appliquée à l' … [ … ]opérateur.

  2. new Fooa une associativité de droite à gauche et pour new Foo()"associativité" ne s'applique pas. Je pense que dans la pratique, cela ne fait aucune différence. Pour plus d'informations, voir cette question SO


Est-ce que l'un est préféré à l'autre?

Sachant tout cela, on peut supposer que new Foo()c'est préférable.

traxium
la source
4
Enfin, quelqu'un qui répond réellement à la question et souligne la différence subtile!
gcampbell
Ouah merci. me rappelle une autre langue en.wikipedia.org/wiki/Brainfuck
John Henckel
Bien expliqué, offre exactement la raison pour laquelle new Foo()il convient de le préférer new Foo. La meilleure réponse jusqu'à présent.
CodeLama
Réponse impressionnante, le tableau de priorité et l'explication qui l'accompagne le rendent parfaitement clair. Heureux que vous expliquiez comment cela permet d'écrire new Object().something()aussi bien (new Object()).something().
LittleTiger
1
Excellente explication mais je ne suis pas d'accord avec la conclusion et je pense toujours que ne pas utiliser de parenthèses est très bien si vous connaissez votre langue. BTW si vous ne connaissez pas la priorité JS, vous pouvez également utiliser le (new Date).toString()même nombre de caractères et plus explicite que new Date().toString.
Jean Vincent
14

Je ne pense pas qu'il y ait de différence lorsque vous utilisez le "nouvel" opérateur. Faites attention à ne pas prendre cette habitude, car ces deux lignes de code ne sont PAS les mêmes:

var someVar = myFunc; // this assigns the function myFunc to someVar
var someOtherVar = myFunc(); // this executes myFunc and assigns the returned value to someOtherVar
jbabey
la source
1
Encore plus problématique si vous avez l'habitude d'omettre les points-virgules. ;-)
RobG
13

Si vous n'avez pas d'arguments à passer, les parenthèses sont facultatives. Les omettre n'est que du sucre syntaxique.

Josh
la source
8
Je dirais du sel syntaxique, mais ymmv.
Thomas Eding
Je dirais que les ajouter s'ils ne sont pas nécessaires est du sucre syntaxique. :)
Cozzbie
8

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

Voici la partie de la spécification ES6 qui définit le fonctionnement des deux variantes. La variante sans parenthèses passe une liste d'arguments vide.

Fait intéressant, les deux formes ont des significations grammaticales différentes. Cela apparaît lorsque vous essayez d'accéder à un membre du résultat.

new Array.length // fails because Array.length is the number 1, not a constructor
new Array().length // 0
client
la source
Il est également bien défini dans ES5 et ES3 - "Retourne le résultat de l'appel de la méthode interne [[Construct]] sur le constructeur, sans fournir d'arguments (c'est-à-dire une liste vide d'arguments)."
Benjamin Gruenbaum
3

Il n'y a aucune différence entre les deux.

Ivan
la source