logical AND and OR operators是JavaScript中唯一的懒惰运算符以及三元conditional operator。它们使用以下规则进行short-circuit evaluation测试:
false && anything === false
true || anything === true
这与在Haskell中实现的方式相同:
(&&) :: Bool -> Bool -> Bool
False && _ = False
True && x = x
(||) :: Bool -> Bool -> Bool
True || _ = True
False || x = x
然而according to MDN logical operators in JavaScript are left associative。这是违反直觉的。在我的拙见中,他们应该是正确的联想。 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文档是否有关于逻辑运算符的关联性的错误信息?
编辑:根据specification逻辑运算符是左关联的:
LogicalANDExpression = BitwiseORExpression
| LogicalANDExpression && BitwiseORExpression
LogicalORExpression = LogicalANDExpression
| LogicalORExpression || LogicalANDExpression
答案 0 :(得分:14)
对于任何体面的编译器,这些运算符的选择关联性几乎无关紧要,输出的代码无论如何都是相同的。是的,解析树是不同的,但发出的代码不一定是。
在我所知道的C系列的所有语言中(Javascript也属于这些语言),逻辑运算符是左关联的。所以真正的问题是,为什么类C语言将逻辑运算符定义为左关联?由于所选择的关联性是无关紧要的(就语义和效率而言),我的怀疑是选择了最“自然”(如“大多数其他运营商使用的”)相关性,尽管我不喜欢有任何消息来支持我的说法。其他可能的解释是左关联运算符使用LALR解析器来解析更少的堆栈空间(现在这不是一个大问题,但可能在C出现时就回来了。)
答案 1 :(得分:5)
请考虑以下代码:
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
。
请记住以下两点:
请注意,上述两点都不会改变单个表达式的解释方式,只会解释复合表达式的方式。相关性发生在更高层次。
运算符关联性以及运算符优先级会对语言的行为产生巨大影响。了解这些差异对于能够使用和快速掌握不同编程语言中的功能差异至关重要。你肯定会对我的问题竖起大拇指,我希望这会澄清一些事情。小心。
答案 2 :(得分:-1)
简单回答:想象一下var i = null; if(i == null || i.getSomeValue())... 当不是左关联时,将首先评估第二个测试,给你一个例外。