为什么JavaScript中的逻辑运算符是关联的?

Aad*_*hah 25 javascript haskell logical-operators associativity ecmascript-5

该逻辑AND和OR运算是在JavaScript中只有懒运营三元一起条件运算符.他们使用以下规则进行短路评估测试:

false && anything === false
true || anything === true
Run Code Online (Sandbox Code Playgroud)

这与在Haskell中实现的方式相同:

(&&) :: Bool -> Bool -> Bool
False && _ = False
True  && x = x

(||) :: Bool -> Bool -> Bool
True  || _ = True
False || x = x
Run Code Online (Sandbox Code Playgroud)

但是根据MDN,JavaScript中的逻辑运算符是左关联的.这是违反直觉的.在我的拙见中,他们应该是正确的联想.Haskell做对了.Haskell中的逻辑运算符是正确的关联:

infixr 3 &&
infixr 2 ||
Run Code Online (Sandbox Code Playgroud)

考虑Haskell中的以下表达式:

False && True && True && True
Run Code Online (Sandbox Code Playgroud)

因为&&在Haskell中是正确关联的,所以上面的表达式相当于:

False && (True && (True && True))
Run Code Online (Sandbox Code Playgroud)

因此,表达式(True && (True && True))评估的内容并不重要.因为第一步False将整个表达式简化为False一步.

现在考虑如果&&是左关联会发生什么.表达式相当于:

((False && True) && True) && True
Run Code Online (Sandbox Code Playgroud)

现在需要3次减少来评估整个表达式:

((False && True) && True) && True
(False && True) && True
False && True
False
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,逻辑运算符更适合于正确关联.这让我想到了我的实际问题:

为什么JavaScript中的逻辑运算符是关联的?ECMAScript规范对此有何看法?JavaScript中的逻辑运算符实际上是正确的关联吗?MDN文档是否有关于逻辑运算符的关联性的错误信息?


编辑:根据规范,逻辑运算符是左关联的:

LogicalANDExpression = BitwiseORExpression
                     | LogicalANDExpression && BitwiseORExpression

LogicalORExpression = LogicalANDExpression
                    | LogicalORExpression || LogicalANDExpression
Run Code Online (Sandbox Code Playgroud)

Ped*_*ues 15

对于任何体面的编译器,这些运算符的选择关联性几乎是无关紧要的,输出的代码无论如何都是相同的.是的,解析树是不同的,但不需要发出的代码.

在我所知道的C系列的所有语言中(Javascript也属于这些语言),逻辑运算符是左关联的.所以真正的问题是,为什么类C语言将逻辑运算符定义为左关联?由于所选择的关联性是无关紧要的(就语义和效率而言),我的怀疑是选择了最"自然"(如"大多数其他运营商使用的")相关性,尽管我不喜欢有任何消息来支持我的说法.其他可能的解释是左关联运算符使用LALR解析器来解析更少的堆栈空间(现在这不是一个大问题,但可能在C出现时回来).


dra*_*112 5

考虑以下代码:

console.log(   true || (false && true) );   // right associative (implicit)
console.log(  (true || false) && true  );   // left associative (Javascript)
Run Code Online (Sandbox Code Playgroud)

这两个例子实际上都会返回相同的结果,但这并不是担心运算符关联性的原因.它与此相关的原因是因为逻辑运算符决定其结果的独特方式.即使所有排列最终都得出相同的最终结论,计算的顺序也会发生变化,这对您的代码会产生重​​大影响.

那么,现在考虑一下:

    var name = "";

    // ----------------------------------------
    // Right associative
    // ----------------------------------------
    name = "albert einstein";
    console.log("RIGHT : %s : %s", true || (false && updateName()), name);

    // ----------------------------------------
    // Left Associative
    // ----------------------------------------
    name = "albert einstein";
    console.log("LEFT  : %s : %s", (true || false) && updateName(), name);

    function updateName() {
        name = "charles manson";
        return true;
    }
Run Code Online (Sandbox Code Playgroud)

输出是:

    RIGHT : true : albert einstein
    LEFT  : true : charles manson
Run Code Online (Sandbox Code Playgroud)

两个表达式都返回true,但只有左关联版本必须调用updateName()才能返回答案.正确的关联版本是不同的.它只评估第一个参数,(false && updateName())因为第二个参数不能改变false为a true.

请记住这两点:

  • 运算符优先级描述了不同运算符类型的复合表达式的嵌套顺序.
  • Operator Associativity描述具有相同运算符优先级的复合表达式的嵌套顺序.

请注意,上述两点都不会改变单个表达式的解释方式,只会改变复合表达式的解释方式.相关性发生在更高的层次上.

运算符关联性以及运算符优先级对语言的行为方式产生巨大影响.了解这些差异对于能够使用和快速掌握不同编程语言中的功能差异至关重要.你肯定会对我的问题竖起大拇指,我希望这会澄清一些事情.照顾自己.