Podemos omitir parênteses ao criar um objeto usando o operador "novo"?

229

Eu vi objetos sendo criados desta maneira:

const obj = new Foo;

Mas pensei que os parênteses não são opcionais ao criar um objeto:

const obj = new Foo();

A maneira anterior de criar objetos é válida e definida no padrão ECMAScript? Existem diferenças entre a maneira anterior de criar objetos e a posterior? Um prefere o outro?

Behrang Saeedzadeh
fonte
Referência atualizada: ECMAScript 2017 §12.3.3 O novo Operador .
#
TL; DR: Cuidado que new a.b()é diferente de new a().b(), em que, no primeiro caso, a.bé primeiramente acedido, ao passo que no último caso, uma nova aé criado pela primeira vez.
Andrew

Respostas:

238

Citando David Flanagan 1 :

Como um caso especial, apenas para o newoperador, o JavaScript simplifica a gramática, permitindo que os parênteses sejam omitidos se não houver argumentos na chamada de função. Aqui estão alguns exemplos usando o newoperador:

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

...

Pessoalmente, eu sempre uso parênteses, mesmo quando o construtor não aceita argumentos.

Além disso, o JSLint pode prejudicar seus sentimentos se você omitir o parêntese. Ele relata Missing '()' invoking a constructore não parece haver uma opção para a ferramenta tolerar a omissão de parênteses.


1 David Flanagan: JavaScript, o Guia Definitivo: 4ª Edição (página 75)

Daniel Vassallo
fonte
6
Por que o JSLint incentiva o uso de parênteses?
Randomblue
11
Eu acho que é apenas considerado mais consistente.
precisa
12
Acho interessante ver que muitos desenvolvedores de JavaScript usam parênteses simplesmente porque "a ferramenta (JSLint) disse a eles para fazê-lo", especialmente considerando que os exemplos em developer.mozilla.org/en-US/docs/Web/JavaScript/Guide /… , De "os caras que inventaram a linguagem <expletive>" não usam parênteses new Classpara construtores sem parâmetros. Se isso não soletrar 'teimoso', eu não sei o que faz ...
ack
52
@ack Bem, seria estranho não ver os inventores da linguagem mostrarem certos recursos de sua linguagem (nesse caso, a opção de omitir parênteses nos construtores). Se eles não tivessem adicionado o recurso, não estaríamos perguntando se deveria ser usado em primeiro lugar. Um motivo prático para não usá-lo é o seguinte: new Object.func()NÃO é equivalente a new Object().func(). Sempre incluindo parênteses, a possibilidade de cometer esse erro é eliminada.
Ndlean
2
Se você deseja eliminar a possibilidade de cometer um erro, deve usar (new Object).func(). Mas considero usar parênteses extras e sinais de igual extras, como no ==vs ===, uma desculpa ruim para não aprender o seu idioma.
Jean Vincent
78

Existem diferenças entre os dois:

  • new Date().toString() funciona perfeitamente e retorna a data atual
  • new Date.toString()lança " TypeError: Date.toString não é um construtor "

Isso acontece porque new Date()e new Datetem precedência diferente. De acordo com o MDN, a parte da tabela de precedência do operador JavaScript em que estamos interessados ​​se parece com:

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

A partir desta tabela, segue-se o seguinte:

  1. new Foo() tem precedência mais alta que new Foo

    new Foo()tem a mesma precedência que o .operador

    new Footem um nível de precedência menor que o .operador

    new Date().toString() funciona perfeitamente porque avalia como (new Date()).toString()

    new Date.toString()lança " TypeError: Date.toString não é um construtor " porque .tem precedência mais alta que new Date(e mais alta que "Chamada de Função") e a expressão é avaliada como(new (Date.toString))()

    A mesma lógica pode ser aplicada ao … [ … ]operador.

  2. new Footem associatividade da direita para a esquerda e para new Foo()"associatividade" não é aplicável. Eu acho que na prática não faz nenhuma diferença. Para obter informações adicionais, consulte esta pergunta do SO


Um prefere o outro?

Sabendo tudo isso, pode-se presumir que new Foo()é o preferido.

traxium
fonte
4
Finalmente, alguém que realmente responde à pergunta e aponta a diferença sutil!
Gcampbell 07/07
uau, obrigado. me lembra de outra língua en.wikipedia.org/wiki/Brainfuck
John Henckel
Bem explicado, oferece exatamente o motivo pelo qual new Foo()deve ser preferido new Foo. A melhor resposta até agora.
CodeLama 27/07
Resposta impressionante, a tabela de precedência e a explicação que a acompanha tornam completamente clara. Que bom que você explica como isso faz bem em escrevernew Object().something() e também (new Object()).something().
LittleTiger
1
Ótima explicação, mas eu discordo da conclusão e ainda acho que não usar parênteses é bom se você souber seu idioma. BTW, se você não conhece a precedência de JS, também pode usar a (new Date).toString()mesma contagem de caracteres e mais explícito quenew Date().toString .
Jean Vincent
14

Eu não acho que exista alguma diferença quando você estiver usando o operador "novo". Tenha cuidado ao entrar nesse hábito, pois essas duas linhas de código NÃO são iguais:

var someVar = myFunc; // this assigns the function myFunc to someVar
var someOtherVar = myFunc(); // this executes myFunc and assigns the returned value to someOtherVar
jbabey
fonte
1
Ainda mais problemático se você costuma omitir ponto e vírgula. ;-)
RobG 01/08/19
13

Se você não tiver argumentos para passar, os parênteses são opcionais. Omiti-los é apenas açúcar sintático.

Josh
fonte
8
Eu diria sal sintático, mas sim.
Thomas Eding
Eu diria que adicioná-los se não forem necessários é açúcar sintático. :)
Cozzbie
8

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

Aqui está a parte da especificação ES6 que define como as duas variantes operam. A variante sem parênteses passa uma lista de argumentos vazia.

Curiosamente, as duas formas têm significados gramaticais diferentes. Isso aparece quando você tenta acessar um membro do resultado.

new Array.length // fails because Array.length is the number 1, not a constructor
new Array().length // 0
convidado
fonte
Também está bem definido no ES5 e no ES3 - "Retorne o resultado da chamada do método interno [[Construct]] no construtor, sem fornecer argumentos (ou seja, uma lista vazia de argumentos)".
Benjamin Gruenbaum 29/05
3

Não há diferença entre os dois.

Ivan
fonte