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?
Citant David Flanagan1:
Comme cas spécial, pour l'opérateur
new
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 utilisant l'opérateurnew
: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)
Il existe des différences entre les deux:
new Date().toString()
fonctionne parfaitement et retourne la date actuellenew Date.toString()
lance " TypeError: Date.toString n'est pas un constructeur "Cela se produit parce que new Date()
et new Date
Ont 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:
new Foo()
a une priorité plus élevée que new Foo
new Foo()
a la même priorité que l'opérateur .
new Foo
A un niveau de priorité inférieur à l'opérateur .
new Date().toString()
fonctionne parfaitement car il est évalué comme (new Date()).toString()
new Date.toString()
lance " TypeError: Date.toString n'est pas un constructeur " car .
a une priorité plus élevée que new Date
(Et supérieur à "Appel de fonction") et l'expression est évaluée comme (new (Date.toString))()
La même logique peut être appliquée à l'opérateur … [ … ]
.
new Foo
A associativité de droite à gauche et pour new Foo()
"associativité" n'est pas applicable. Je pense que dans la pratique, cela ne fait aucune différence. Pour plus d'informations, voir this SO question
Est-ce que l'un est préféré à l'autre?
Sachant tout cela, on peut supposer que new Foo()
est préférable.
Je ne pense pas qu'il y ait de différence lorsque vous utilisez le "nouveau" opérateur. Soyez prudent lorsque vous prenez 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
Si vous n'avez pas d'arguments à passer, les parenthèses sont facultatives. Les omettre n'est que du sucre syntaxique.
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
Il n'y a aucune différence entre les deux.