使用 "new" 操作符创建对象时,我们可以省略括号吗?

276

我曾经看到过通过这种方式创建对象:

const obj = new Foo;

但是我认为在创建对象时,括号是不可选的:

const obj = new Foo();

使用之前的创建对象方式是否在ECMAScript标准中有效和定义?之前的创建对象方式和后来的方式有什么区别?是否有一种方法比另一种更可取?


http://www.ecma-international.org/ecma-262/5.1/#sec-11.2.2 - SheetJS
更新的参考资料:ECMAScript 2017 §12.3.3 新操作符 - RobG
1
TL;DR: 注意,new a.b()new a().b() 是不同的。在前者中,首先访问 a.b,而在后者中,首先创建一个新的 a - Andrew
6个回答

273

引用David Flanagan1的话:

作为一个特例,仅适用于new操作符的情况下,JavaScript简化了语法,如果函数调用中没有参数,括号可以省略。以下是一些使用new操作符的示例:

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

...
个人上,即使构造函数不需要参数,我也会始终使用括号。
此外,如果省略了括号,则JSLint可能会让你感到不舒服。它将报告“缺少'()'调用构造函数”,并且似乎没有选项可以让该工具容忍括号的省略。
1 David Flanagan:JavaScript权威指南:第4版(第75页)

8
JSLint为什么鼓励使用括号? - Randomblue
12
我猜这被认为更加一致。 - Daniel Vassallo
14
我觉得有趣的是,许多JavaScript开发人员仅仅因为工具(JSLint)告诉他们这么做,就使用括号,特别是考虑到在https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Details_of_the_Object_Model上的示例来自“发明了该语言的家伙们”,并没有在不带参数的构造函数`new Class`上使用任何括号。如果这不表明"固执己见",我也不知道还有什么能表明... - ack
63
@ack:嗯,如果语言的发明者不展示他们语言的某些特性(在这种情况下就是构造函数中省略括号的选项),那么就显得有些奇怪。如果他们没有添加这个特性,我们就不会问是否应该首先使用它。不使用它的一个实际原因是:new Object.func()不等同于new Object().func()。通过始终包括括号,可以消除犯这种错误的可能性。 - nmclean
2
如果你想消除犯错的可能性,你应该使用(new Object).func()。但是我认为使用额外的括号和额外的等号,例如=====,是不学习你的语言的借口。 - Jean Vincent
显示剩余4条评论

107

这两者之间存在差异:

  • new Date().toString()可以正常工作并返回当前日期
  • new Date.toString()会抛出“TypeError: Date.toString不是一个构造函数”的错误

这是因为new Date()new Date有不同的优先级。根据MDN,我们感兴趣的JavaScript运算符优先级表如下:

优先级 运算符类型 结合性 运算符
18 成员访问
计算成员访问
new(带参数列表)
从左到右
从左到右
不适用
… . …
… [ … ]
new … ( … )
17 函数调用
new(不带参数列表)
从左到右
从右到左
… ( … )
new …

由此可见:

  1. new Foo()的优先级高于new Foo

    new Foo().运算符具有相同的优先级

    new Foo.运算符的优先级低一级

    new Date().toString()可以正常工作,因为它被解析为(new Date()).toString()

new Date.toString()抛出“TypeError: Date.toString不是构造函数”,因为.具有比new Date(以及比“函数调用”更高的优先级),因此表达式被解释为(new (Date.toString))()

相同的逻辑也可以应用于…[…]操作符。

  • new Foo具有从右到左的结合性,而对于new Foo(),“结合性”不适用。我认为在实践中这没有任何区别。有关详细信息,请参见 SO问题


  • 其中一个更受偏爱吗?

    了解了所有这些,可以假设new Foo()更受欢迎。


    11
    终于有人回答了问题并指出了微妙的差异! - gcampbell
    哇,谢谢你。这让我想起了另一种语言 https://en.wikipedia.org/wiki/Brainfuck - John Henckel
    讲解得很清楚,准确地说明了为什么应该优先使用new Foo()而不是new Foo。到目前为止最好的答案。 - CodeLama
    非常棒的答案,优先级表和相关解释使其完全清晰。很高兴你解释了这使得写new Object().something()以及(new Object()).something()都可以。 - LittleTiger
    1
    很好的解释,但我不同意结论,并且仍然认为如果你熟悉你的语言,不使用括号也是可以的。顺便说一句,如果你不知道JS优先级,你也可以使用(new Date).toString(),字符数相同,并且比new Date().toString更明确。 - Jean Vincent
    显示剩余2条评论

    14

    我认为在使用"new"运算符时并没有任何区别。但是要注意不要形成习惯,因为下面这两行代码不相同

    var someVar = myFunc; // this assigns the function myFunc to someVar
    var someOtherVar = myFunc(); // this executes myFunc and assigns the returned value to someOtherVar
    

    2
    如果你有省略分号的习惯,那么问题就更加棘手了。;-) - RobG

    13

    如果没有需要传递的参数,括号是可选的。省略它们只是一种语法糖。


    14
    我会说“句法盐”,但你可能有不同的看法。 - Thomas Eding
    如果它们不是必需的,我会说添加它们只是语法糖。 :) - Cozzbie

    8

    在 ES5 和 ES3 中都有很好地定义 - “返回调用构造函数的[[Construct]]内部方法的结果,不提供参数(即空参数列表)。” - Benjamin Gruenbaum

    2
    这两者之间没有区别。

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接