【问题标题】:Why does "new Date().toString()" work given Javascript operator precedence?为什么“new Date().toString()”在给定 Javascript 运算符优先级的情况下工作?
【发布时间】:2013-07-09 09:30:29
【问题描述】:

MDN states,Javscript 中有两个运算符共享最高优先级:

  • 左关联成员运算符:foo.bar
  • 右关联新运算符:new Foo()

我通常明确地将两者分开:(new Date()).toString()
但我经常看到他们两个结合在一起:new Date().toString()

根据this answer,第二种方法起作用的原因是,当两个运算符具有相同的优先级时,第二个运算符的关联性很重要。在这种情况下,成员运算符是左关联的,这意味着首先计算 new Date()

但是,如果是这样,那为什么new Date.toString() 会失败?毕竟,new Datejust syntactic sugar 对应于 new Date()。上面的论点说它应该有效,但显然无效。

我错过了什么?

【问题讨论】:

  • 因为语法糖没有扩展到实例化和成员访问。这取决于编译器。有点类似的场景:VB.NET 有Dim x As New ...,但我不相信我们可以做到Dim x As New Date().ToString()。我们可以做Dim x As String = new Date().ToString()
  • () 运算符将在. 访问它的属性之前执行构造函数。
  • @ps2goat 你是对的,但根据下面的答案,这是由于指定的语法而不是编译器的奇思妙想。
  • 'new' 的语法规则很复杂,很容易导致错误。我首选的解决方案是让“new”成为一个静态方法,它在内部调用“new this (...)”。然后创建新实例的语法看起来就像任何其他方法调用一样,我不必三思而后行。例如我可以写: MyDate..new().toString() .

标签: javascript parsing operator-precedence


【解决方案1】:

我是写“Disambiguation of expressions with neighboring operators of different associativity and same precedence”的问题和答案的人,当我写下我的脑海中没有 JavaScript。

我考虑的语言是 Haskell,它是一种函数式编程语言。这种语言中的运算符只是简单的函数,并且更容易推理。但是,我以一种不假设任何编程语言的方式编写了我的答案。

另一方面,JavaScript 是一种传统的编程语言,JavaScript 中的表达式是基于复杂的解析规则来消除歧义的,这些规则与 Haskell 使用的解析规则非常不同。

尤其是 JavaScript 解析规则似乎很贪婪。以你的第一个例子为例:

new Date().toString()

这里Date之后的函数调用屏蔽了成员操作符Date。因此,贪婪的new 仍然只能在Date 而不是Date().toString 上运行。因此我们有:

((new Date()).toString)()

在第二个例子中我们有:

new Date.toString()

Date 之后没有函数调用来屏蔽成员运算符。因此,new 贪婪地对表达式Date.toString 进行操作。因此我们有:

(new (Date.toString))()

@thg435 的回答支持了这一说法。重点是我在讨论一个与 JavaScript 解析器实现的完全不同的正式系统。我讨论的正式系统将运算符和操作数都视为一等值。

【讨论】:

    【解决方案2】:

    syntax

    MemberExpression :
        PrimaryExpression
        FunctionExpression
        MemberExpression [ Expression ]
        MemberExpression . IdentifierName
        new MemberExpression Arguments
    

    new foo().bar 无法解析为 new (foo().bar),因为 foo().bar 不是 MemberExpression。此外,出于同样的原因,new foo() 不能被解析为new (foo())。相反,new foo.bar解析为new (foo.bar),因为foo.bar 是一个有效的MemberExpression(解释(new foo).bar 是不可能的,因为语法是贪婪的)。

    也就是说,优先规则是:dot beats new,new beats call (parens)。

    .  -> new -> ()
    

    此外,直接查看语法可以揭开将new Foo 变成new Foo() 的语法糖的神秘面纱。就是 NewExpression ← new NewExpression ← new PrimaryExpression:

    NewExpression :
        MemberExpression
        new NewExpression
    

    【讨论】:

    • 难道foo() 不是一个FunctionExpression,使foo().bar 按照这个定义成为一个MemberExpression?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-01-16
    • 2011-10-25
    • 2018-06-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多