为什么JavaScript中的逻辑运算符是左关联的?
是JavaScript中唯一的惰性运算符以及三元运算符。使用以下规则对其进行测试:为什么JavaScript中的逻辑运算符是左关联的?,javascript,haskell,logical-operators,ecmascript-5,associativity,Javascript,Haskell,Logical Operators,Ecmascript 5,Associativity,是JavaScript中唯一的惰性运算符以及三元运算符。使用以下规则对其进行测试: false && anything === false true || anything === true 这与Haskell中的实现方式相同: (&&) :: Bool -> Bool -> Bool False && _ = False True && x = x (||) :: Bool -> Bool -> Bo
false && anything === false
true || anything === true
这与Haskell中的实现方式相同:
(&&) :: Bool -> Bool -> Bool
False && _ = False
True && x = x
(||) :: Bool -> Bool -> Bool
True || _ = True
False || x = x
False && True && True && True
不过。这是违反直觉的。依我拙见,他们应该是对的。哈斯克尔做了正确的事。Haskell中的逻辑运算符是右关联的:
infixr 3 &&
infixr 2 ||
LogicalANDExpression = BitwiseORExpression
| LogicalANDExpression && BitwiseORExpression
LogicalORExpression = LogicalANDExpression
| LogicalORExpression || LogicalANDExpression
考虑Haskell中的以下表达式:
(&&) :: Bool -> Bool -> Bool
False && _ = False
True && x = x
(||) :: Bool -> Bool -> Bool
True || _ = True
False || x = x
False && True && True && True
因为&&
在Haskell中是右关联的,所以上述表达式相当于:
False && (True && (True && True))
((False && True) && True) && True
因此,表达式(True&&(True&&True))
的计算结果是什么并不重要。由于第一个False
,整个表达式在一个步骤中简化为False
现在考虑如果<代码> & & <代码>是关联的会发生什么。该表达式相当于:
False && (True && (True && True))
((False && True) && True) && True
现在需要3次缩减才能计算整个表达式:
((False && True) && True) && True
(False && True) && True
False && True
False
正如您所看到的,逻辑运算符具有右关联性更有意义。这就引出了我的实际问题:
为什么JavaScript中的逻辑运算符是左关联的?ECMAScript规范对此有何解释?JavaScript中的逻辑运算符实际上是正确的吗?MDN文档是否包含有关逻辑运算符关联性的错误信息
编辑:根据逻辑运算符左关联:
infixr 3 &&
infixr 2 ||
LogicalANDExpression = BitwiseORExpression
| LogicalANDExpression && BitwiseORExpression
LogicalORExpression = LogicalANDExpression
| LogicalORExpression || LogicalANDExpression
对于任何一个像样的编译器来说,这些操作符所选择的关联性几乎是不相关的,并且输出的代码将是相同的。是的,解析树是不同的,但是发出的代码不需要是相同的
在我所知道的所有C族语言(Javascript也属于C族)中,逻辑运算符都是左关联的。因此,真正的问题是,为什么类C语言将逻辑运算符定义为左关联?由于所选择的关联性是不相关的(就语义和效率而言),我怀疑选择了最“自然”(如“其他大多数操作符使用的”)的关联性,尽管我没有任何来源来支持我的主张。另一种可能的解释是,左关联运算符使用LALR解析器解析时占用的堆栈空间更少(这在现在不是一个大问题,但可能是在C出现时);如果(i==null | | i.getSomeValue())。。。
当没有关联时,第二个测试将首先进行评估,给出一个异常。考虑以下代码:
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
这两个示例实际上返回相同的结果,但这不是担心运算符关联性的原因。它之所以与此相关,是因为逻辑运算符决定其结果的独特方式。即使所有排列最终得出相同的最终结论,计算顺序也会改变,这可能会对代码产生重大影响
<强>所以现在考虑一下:< /强>
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
输出为:
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
记住这两点:
- 运算符优先级描述了不同运算符类型的复合表达式的嵌套顺序李>
- 运算符关联性描述具有相同运算符优先级的复合表达式的嵌套顺序李>
运算符关联性以及运算符优先级对语言的行为有着巨大的影响。理解这些差异对于能够使用和快速掌握不同编程语言的功能差异至关重要。你的问题肯定会得到我的赞许,我希望这能澄清一些事情。小心。结果(包括短路)在任何一种方式下都是一样的,不是吗?如果关联性对结果没有任何影响,编译器可以在任何一种方式下进行。规范的作者可能并不担心这一点,因为这无关紧要。从您链接到的MDN表来看,他们似乎只是让所有二进制运算符保持关联,除了赋值。在大多数情况下,选择是任意的,因为操作是可交换的。我想我的意思是它们是关联的和可交换的。忽略短路,
a和&b
相当于b和&a
。大多数交换运算符也是关联的,但我发现:@Ankur不,它不会。由于逻辑AND和OR的定义方式,第一个操作数始终求值,第二个操作数仅在第一个求值不短路时求值。在计算第一个操作数之前,不能先计算第二个操作数。因此,即使最后一个操作数是False
,您仍然需要计算第一个操作数,而第一个操作数又需要计算其第一个操作数,依此类推。这不是问题所在。关联性仅在链中具有多个运算符的表达式中起作用,如if(i==null | | | i.isBad()| | i.getSomeValue()),可以解析为(i==null | | i.isBad())| i.getSomeValue()
(左关联)或i==null | i.isBad()| i.getSomeValue()
(右关联).关联性并不决定首先计算哪个操作数。它们决定了操作符的定义方式