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?



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)


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.


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

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.


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

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

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.