¿Podemos omitir paréntesis al crear un objeto utilizando el operador "nuevo"?

229

He visto objetos creados de esta manera:

const obj = new Foo;

Pero pensé que los paréntesis no son opcionales al crear un objeto:

const obj = new Foo();

¿Es la forma anterior de crear objetos válida y definida en el estándar ECMAScript? ¿Hay alguna diferencia entre la forma anterior de crear objetos y la posterior? ¿Se prefiere uno sobre el otro?

Behrang Saeedzadeh
fuente
Referencia actualizada: ECMAScript 2017 §12.3.3 El nuevo Opertator .
RobG
TL; DR: Cuidado con que new a.b()es diferente de new a().b(), en que, en el primer caso, a.bprimero se accede, mientras que en el último caso, aprimero se crea un nuevo .
Andrew

Respuestas:

238

Citando a David Flanagan 1 :

Como caso especial, solo para el newoperador, JavaScript simplifica la gramática al permitir que se omita el paréntesis si no hay argumentos en la llamada a la función. Aquí hay algunos ejemplos que usan el newoperador:

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

...

Personalmente, siempre uso el paréntesis, incluso cuando el constructor no toma argumentos.

Además, JSLint puede herir tus sentimientos si omites el paréntesis. Informa Missing '()' invoking a constructor, y no parece haber una opción para que la herramienta tolere la omisión de paréntesis.


1 David Flanagan: JavaScript, la guía definitiva: 4a edición (página 75)

Daniel Vassallo
fuente
66
¿Por qué JSLint fomenta el uso de paréntesis?
Randomblue
11
Supongo que solo se considera más consistente.
Daniel Vassallo
12
Me parece interesante ver que muchos desarrolladores de JavaScript usan paréntesis simplemente porque "la herramienta (JSLint) les dijo que lo hicieran", especialmente considerando que los ejemplos en developer.mozilla.org/en-US/docs/Web/JavaScript/Guide / ... , de "los tipos que inventaron el lenguaje <expletivo>" no usan paréntesis new Classpara los constructores sin parámetros. Si esto no explica 'obstinado', no sé lo que hace ...
ACK
52
@ack Bueno, sería extraño no ver a los inventores del lenguaje mostrar ciertas características de su idioma (en este caso, la opción de omitir paréntesis en los constructores). Si no hubieran agregado la función, no estaríamos preguntando si debería usarse en primer lugar. Una razón práctica para no usarlo es esta: new Object.func()NO es equivalente a new Object().func(). Al incluir siempre paréntesis, se elimina la posibilidad de cometer este error.
nmclean el
2
Si desea eliminar la posibilidad de cometer un error, debe utilizarlo (new Object).func(). Pero considero que usar paréntesis y signos de igualdad adicionales, como en ==vs ===, es una mala excusa para no aprender su idioma.
Jean Vincent
78

Hay diferencias entre los dos:

  • new Date().toString() funciona perfectamente y devuelve la fecha actual
  • new Date.toString()lanza " TypeError: Date.toString no es un constructor "

Sucede porque new Date()y new Datetienen una precedencia diferente. Según MDN, la parte de la tabla de precedencia de operadores de JavaScript que nos interesa se ve así:

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

De esta tabla se deduce que:

  1. new Foo() tiene mayor prioridad que new Foo

    new Foo()tiene la misma precedencia que el .operador

    new Footiene un nivel de precedencia menor que el .operador

    new Date().toString() funciona perfectamente porque se evalúa como (new Date()).toString()

    new Date.toString()arroja " TypeError: Date.toString no es un constructor " porque .tiene mayor prioridad que new Date(y mayor que "Llamada de función") y la expresión se evalúa como(new (Date.toString))()

    La misma lógica se puede aplicar al … [ … ]operador.

  2. new Footiene asociatividad de derecha a izquierda y para new Foo()"asociatividad" no es aplicable. Creo que en la práctica no hace ninguna diferencia. Para información adicional vea esta pregunta SO


¿Se prefiere uno sobre el otro?

Sabiendo todo eso, se puede suponer que new Foo()se prefiere.

traxio
fuente
44
Finalmente, ¡alguien que realmente responde la pregunta y señala la sutil diferencia!
gcampbell
Wow gracias. me recuerda a otro idioma en.wikipedia.org/wiki/Brainfuck
John Henckel
Bien explicado, ofrece exactamente la razón por la cual new Foo()debería preferirse new Foo. La mejor respuesta hasta ahora.
CodeLama
Impresionante respuesta, la tabla de precedencia y la explicación que lo acompaña lo deja completamente claro. Me alegra que expliques cómo eso hace que esté bien escribir new Object().something()así (new Object()).something().
LittleTiger
1
Gran explicación, pero no estoy de acuerdo con la conclusión y sigo pensando que no usar paréntesis está bien si conoces tu idioma. Por cierto, si no conoce la precedencia de JS, también podría usar el (new Date).toString()mismo recuento de caracteres y más explícito que new Date().toString.
Jean Vincent
14

No creo que haya ninguna diferencia cuando está utilizando el operador "nuevo". Tenga cuidado con este hábito, ya que estas dos líneas de código NO son lo mismo:

var someVar = myFunc; // this assigns the function myFunc to someVar
var someOtherVar = myFunc(); // this executes myFunc and assigns the returned value to someOtherVar
jbabey
fuente
1
Aún más problemático si tienes la costumbre de omitir punto y coma. ;-)
RobG
13

Si no tiene argumentos para pasar, los paréntesis son opcionales. Omitirlos es solo azúcar sintáctico.

Josh
fuente
8
Yo diría sal sintáctica, pero mmm.
Thomas Eding
Diría que agregarlos si no son necesarios es azúcar sintáctica. :)
Cozzbie
8

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

Aquí está la parte de la especificación ES6 que define cómo funcionan las dos variantes. La variante sin paréntesis pasa una lista de argumentos vacía.

Curiosamente, las dos formas tienen significados gramaticales diferentes. Esto aparece cuando intenta acceder a un miembro del resultado.

new Array.length // fails because Array.length is the number 1, not a constructor
new Array().length // 0
invitado
fuente
También está bien definido en ES5 y ES3: "Devuelve el resultado de llamar al método interno [[Construct]] en el constructor, sin proporcionar argumentos (es decir, una lista vacía de argumentos)".
Benjamin Gruenbaum
3

No hay diferencia entre los dos.

Ivan
fuente