繁体   English   中英

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

[英]Why are logical operators in JavaScript left associative?

逻辑AND和OR运算符是JavaScript中唯一的延迟运算符以及三元条件运算符 他们使用以下规则进行短路评估测试:

false && anything === false
true || anything === true

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

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

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

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

infixr 3 &&
infixr 2 ||

考虑Haskell中的以下表达式:

False && True && True && True

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

False && (True && (True && True))

因此,表达式(True && (True && True))评估结果并不重要。 由于第一个False ,整个表达式在一个步骤中减少为False

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

((False && True) && True) && True

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

((False && True) && True) && True
(False && True) && True
False && True
False

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

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


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

LogicalANDExpression = BitwiseORExpression
                     | LogicalANDExpression && BitwiseORExpression

LogicalORExpression = LogicalANDExpression
                    | LogicalORExpression || LogicalANDExpression

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

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

考虑以下代码:

console.log(   true || (false && true) );   // right associative (implicit)
console.log(  (true || false) && true  );   // left associative (Javascript)

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

那么,现在考虑一下:

    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;
    }

输出是:

    RIGHT : true : albert einstein
    LEFT  : true : charles manson

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

请记住这两点:

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

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

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

简单回答:想象一下var i = null; if(i == null || i.getSomeValue())...当不是左关联时,将首先评估第二个测试,给出异常。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM