简体   繁体   English

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

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

The logical AND and OR operators are the only lazy operators in JavaScript along with the ternary conditional operator . 逻辑AND和OR运算符是JavaScript中唯一的延迟运算符以及三元条件运算符 They are tested for short-circuit evaluation using the following rules: 他们使用以下规则进行短路评估测试:

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

This is the same way it is implemented in Haskell: 这与在Haskell中实现的方式相同:

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

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

However according to MDN logical operators in JavaScript are left associative . 但是根据MDN,JavaScript中的逻辑运算符是左关联的 This is counter intuitive. 这是违反直觉的。 In my humble opinion they should be right associative. 在我的拙见中,他们应该是正确的联想。 Haskell does the right thing. Haskell做对了。 Logical operators in Haskell are right associative: Haskell中的逻辑运算符是正确的关联:

infixr 3 &&
infixr 2 ||

Consider the following expression in Haskell: 考虑Haskell中的以下表达式:

False && True && True && True

Because && is right associative in Haskell the above expression is equivalent to: 因为&&在Haskell中是正确关联的,所以上面的表达式相当于:

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

Hence it doesn't matter what the expression (True && (True && True)) evaluates to. 因此,表达式(True && (True && True))评估结果并不重要。 Because of the first False the entire expression is reduced to False in a single step. 由于第一个False ,整个表达式在一个步骤中减少为False

Now consider what would happen if && was left associative. 现在考虑如果&&是关联的会发生什么。 The expression would be equivalent to: 表达式相当于:

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

It would now take 3 reductions to evaluate the entire expression: 现在需要3次减少来评估整个表达式:

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

As you can see it makes more sense for logical operators to be right associative. 正如您所看到的,逻辑运算符更适合于正确关联。 This brings me to my actual question: 这让我想到了我的实际问题:

Why are logical operators in JavaScript left associative? 为什么JavaScript中的逻辑运算符是关联的? What does the ECMAScript specification have to say about this? ECMAScript规范对此有何看法? Are logical operators in JavaScript actually right associative? JavaScript中的逻辑运算符实际上是正确的关联吗? Does the MDN docs have incorrect information about the associativity of logical operators? MDN文档是否有关于逻辑运算符的关联性的错误信息?


Edit: According to the specification logical operators are left associative: 编辑:根据规范,逻辑运算符是左关联的:

LogicalANDExpression = BitwiseORExpression
                     | LogicalANDExpression && BitwiseORExpression

LogicalORExpression = LogicalANDExpression
                    | LogicalORExpression || LogicalANDExpression

For any decent compiler the chosen associativity of these operators are pretty much irrelevant, and the outputted code will be the same regardless. 对于任何体面的编译器,这些运算符的选择关联性几乎是无关紧要的,输出的代码无论如何都是相同的。 Yes, the parse tree is different, but the emitted code doesn't need to be. 是的,解析树是不同的,但不需要发出的代码。

In all of the languages of the C family that I know of (to which Javascript also belongs), the logical operators are left associative. 在我所知道的C系列的所有语言中(Javascript也属于这些语言),逻辑运算符是左关联的。 So the real question becomes, why do C-like languages define logical operators as left-associative? 所以真正的问题是,为什么类C语言将逻辑运算符定义为左关联? Since the chosen associativity is irrelevant (in terms of semantic, and in terms of efficiency), my suspicion is that the most "natural" (as in "what the majority of the other operators use") associativity was chosen, although I don't have any sources to back up my claims. 由于所选择的关联性是无关紧要的(就语义和效率而言),我的怀疑是选择了最“自然”(如“大多数其他运营商使用的”)相关性,尽管我不喜欢有任何消息来支持我的说法。 Other possible explanation is that left associative operators take less stack space to parse using an LALR parser (which is not a big concern nowadays, but probably was back when C appeared). 其他可能的解释是左关联运算符使用LALR解析器来解析更少的堆栈空间(现在这不是一个大问题,但可能在C出现时回来)。

Consider this code: 考虑以下代码:

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

Both of those examples do in fact return the same result, but that is not the reason to worry about operator associativity. 这两个例子实际上都会返回相同的结果,但这并不是担心运算符关联性的原因。 The reason it's relevant here is because of the unique way that logical operators determine their outcomes. 它与此相关的原因是因为逻辑运算符决定其结果的独特方式。 Even though all permutations ultimately make the same final conclusion, the order of the calculations change, and that can have big implications for your code. 即使所有排列最终都得出相同的最终结论,计算的顺序也会发生变化,这对您的代码会产生重​​大影响。

So, now consider this: 那么,现在考虑一下:

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

The output is: 输出是:

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

Both expressions return true, but only the left associated version had to call updateName() in order to return an answer. 两个表达式都返回true,但只有左关联版本必须调用updateName()才能返回答案。 The right associated version is different. 正确的关联版本是不同的。 It only evaluates the first argument in (false && updateName()) because the second argument can't change the false into a true . 它只计算(false && updateName())的第一个参数,因为第二个参数不能将false更改为true

Remember these two points: 请记住这两点:

  • Operator Precedence describes the nesting order of compound expressions of different operator types. 运算符优先级描述了不同运算符类型的复合表达式的嵌套顺序。
  • Operator Associativity describes the nesting order of compound expressions with the same operator precedence. Operator Associativity描述具有相同运算符优先级的复合表达式的嵌套顺序。

Notice that neither of the two points above change the way an individual expression is interpreted, only the way compound expressions are interpreted. 请注意,上述两点都不会改变单个表达式的解释方式,只会改变复合表达式的解释方式。 Associativity happens at a higher level. 相关性发生在更高的层次上。

Operator associativity, as well as operator precedence, have enormous impact in how a language behaves. 运算符关联性以及运算符优先级对语言的行为方式产生巨大影响。 Understanding what those differences are is critical to being able to use and quickly grasp the functional differences in diverse programming languages. 了解这些差异对于能够使用和快速掌握不同编程语言中的功能差异至关重要。 You definitely get a thumbs up from me for your question and I hope this clarifies things a little bit. 你肯定会对我的问题竖起大拇指,我希望这会澄清一些事情。 Take care. 照顾自己。

Simple answer: Imagine var i = null; 简单回答:想象一下var i = null; if (i == null || i.getSomeValue()) ... When not being left associative, the second test would be evaluated first giving you an exception. if(i == null || i.getSomeValue())...当不是左关联时,将首先评估第二个测试,给出异常。

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

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