为啥`false && true ||真`评估为真?
Posted
技术标签:
【中文标题】为啥`false && true ||真`评估为真?【英文标题】:Why does `false && true || true` evaluate to true?为什么`false && true ||真`评估为真? 【发布时间】:2014-02-16 05:15:23 【问题描述】:根据MDN Logical Operators页面:
false && 短路评估为 false。
鉴于此信息,我预计 false && true || true
的评估结果为 false。然而,这种情况并非如此。预期结果 (false
) 仅在语句编写如下时给出:
false && (true || true)
我和一位同事试图解决这个问题,我们能想到的最接近的事情是该语句正在按优先级顺序进行评估。根据MDN Operator Precedence logical-and
比logical-or
具有更高的精确度,这表明条件被评估为好像false && true
是单个语句,然后继续确定false || true
的布尔条件,然后true
。写出来,这将是:
(false && true) || true
这里出了点问题。要么是文档,要么是 javascript 解析逻辑,要么是我的解释。
编辑:
我添加了一个赏金,因为给出的答案都没有真正理解这个问题。如上所述:MDN Logical Operators 页面准确地指出:“假 && 任何东西都被短路评估为假。”
“任何东西”的意思是“任何东西”!
【问题讨论】:
计算结果为false
。您确定这不是更复杂操作的一部分吗?
没有错,您展示的第二种形式是它的工作方式, (false && true) 评估为 false,因此最终结果为:false || true
这将给出 true,因为其中至少有一个是真的。
当你强调“任何东西”的意思是“任何东西”!肯定是正确的 - any RHS 表达式是短路的并且没有被评估 - “anything” not 表示“anything and everything”。
任何事情都意味着任何事情。这意味着接下来的事情,无论它可能是什么。正如@BrianNorth 所说,这并不意味着一切。如果它确实意味着一切,那么您的程序会在遇到false &&
时立即停止,因为它只会使程序的其余部分短路。
(false && true) || true
与 false && (true || true)
不同。你认为你的代码代表什么? false && anything
是假的。但是你有一个额外的|| true
。
【参考方案1】:
你的困惑都归结为对优先级的误解。
一个数学类比是“零乘以任何等于零”。考虑以下表达式:
0 x 100 + 5
在任何编程语言或像样的计算器中,它的计算结果为 5。“零乘以任何东西”公理是正确的 - 但在这种情况下,“任何东西”是 100
,而不是 100 + 5
!要了解原因,请将其与以下内容进行比较:
5 + 0 x 100
在开头或结尾添加 5 都没有关系 - 运算符优先级规则消除了语句中的歧义。
在 JavaScript 布尔逻辑中,&&
的优先级高于 ||
。由于每个算子都是commutative,所以写
false && true || true
和写完全一样
true || false && true
【讨论】:
我认为这最好地说明了正在发生的原理。不过,对我来说,由于我之前对短路的理解,这仍然令人失望。 @netinept - 我相信我的回答解释了短路的行为方式你会同意:***.com/a/21394123/901641(看看它的结尾。) @ArtOfWarfare 是的,我同意你的评估。我正在等待奖励赏金并努力保持开放的心态,因为有很多好的答案,而且很难选择最好的。 精彩的解释,不过我一开始并不相信,只好在developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…查看优先级【参考方案2】:你看错了优先级。
&& 先完成,然后 ||所以它看起来就是你写的:
(false && true) || true
所以MDN链接是正确的。
【讨论】:
那么短路呢?它说“假 && 任何东西都被短路评估为假。” @netinept - false 或 true 始终为 true。假的,任何事情都必须是假的,因为它永远不会是真的。优先级是评估事物的顺序,因此您可以根据优先级将括号括在方程式周围,然后看看它是什么样子。 @netinept 运算符优先于短路【参考方案3】:该语言通过将表达式解析为抽象语法树来工作。您的表达式 false && true || true
被解析为:
||
/ \
&& true
/ \
false true
只有在构建 AST 之后,才能进行短路评估。
false && 短路评估为 false。
引用仅适用于false
和anything
是&&
节点的子树的有效子树,如下所示:
&&
/ \
false true
这意味着只有false && true
的短路评估为false
,而结果false || true
评估为true
【讨论】:
【参考方案4】:JavaScript 会这样解析这个表达式:
1) false && true //evaluates to false
2) false || true //equals true. The first false is the result above
很容易追踪 JavaScript 是如何以这种方式解析整个表达式的:
(function()alert(1); return false; )() &&
(function()alert(2); return true; )() ||
(function()alert(3); return true; )()
【讨论】:
我真的很喜欢你追踪它的方式。我从来没有想过那样写任何东西。如果它有效(并且我认为它有效),那不仅是光滑的,而且我需要更仔细地研究,因为它是一个非常有趣的结构。 感谢您的反馈。 =) 这是一个巧妙的技巧,但并不能真正回答问题。【参考方案5】:让我们这样说吧。写这篇 MDN 文章的人措辞很糟糕/犯了一个小错误
正如你指出的那样:
false && 短路评估为 false。
任何理性的人都会认为任何东西都是任何复杂性的逻辑表达。但是,这将是错误的阅读方式(与布尔逻辑规则相矛盾)。
false && true || true == true
另外两位回答者已经解释了为什么会这样。这只是逻辑运算符 && 和 || 的顺序。先处理&&,再处理||处理完毕
现在,回到糟糕的措辞。他们应该说的是以下。请注意我添加的额外括号。
false && (anything) is short-circuit evaluated to false.
我在这里使用括号来表明逻辑表达式的其余部分应该独立于“假”部分进行评估。如果它是独立评估的,那么短路它就可以正常工作,因为 false && == false。
但是,一旦我们有一些无法独立评估的表达式,那么原始短语就不起作用了。
例如false && true || true
不能独立评估,因为顺序或逻辑操作。不过false && doSomething()
可以。
【讨论】:
我完全同意文档应该有(anything)
而不是anything
。
其实是开源文档。我相信如果您在那里创建一个帐户,您应该可以对其进行编辑。【参考方案6】:
首先考虑这个基本的算术表达式:
A * B + C
乘法优先,所以结果是
(A * B) + C
同样,&&
优先,所以
A && B || C
等同于:
(A && B) || C
就短路而言,如果A
是false
,则可能被短路的Anything
是B
。所以如果你有类似的东西:
(false && takesALongTimeToDetermine()) || true
它会跳过评估takesALongTimeToDetermine()
我应该注意这一点:短路永远不会改变答案。这是一种优化。由于&&
只能在两个操作数都是true
的情况下计算为true
,因此如果第一个操作数是false
,它就不会检查第二个操作数是什么,因为它已经知道答案是false
。使用||
进行倒数优化:如果第一个操作数是true
,它将返回true
,而不考虑第二个操作数的值。 这种优化的存在是为了让您的代码运行得更快 - 它永远不会改变答案。在 MDN 中提到它的唯一原因是,如果您的 && 有一个操作数需要比另一个操作数更长的时间来计算,那么您应该将它列在第二位。 (这也很重要,因为如果takesALongTimeToDetermine()
除了返回一个值之外,IE 还做了其他事情,如果它打印了您期望的日志语句,那么如果函数短路,您将看不到它。但这仍然没有与&&
返回的答案有关。)
【讨论】:
如果短路部分有副作用,短路可以改变答案。考虑以下内容:function changeX() x=10; return false;; x=5
。 false && changeX() || x
计算结果为 5(因为 changeX 短路),而true && changeX() || x
计算结果为 10,因为 changeX 没有短路。
@Tibos - &&
短路不会改变 &&
返回的答案。不过,您的示例很有趣-直到我刚才检查了 MDN 文档,我才意识到 &&
和 ||
不会返回布尔值。【参考方案7】:
你说得对,MDN Logical Operators 上的措辞有点模棱两可。
您不应该将 anything 视为 &&
之后的任何内容,而应将其视为 &&
的任何右侧运算符.
&&
需要 2 个表达式:一个在 &&
的左侧,一个在右侧。
在您的示例中:false && true || true
,false 是左侧表达式,第一个 true 是右侧表达式。以下|| true
不是&&
运算符将评估的内容的一部分。
短路意味着不会浪费循环评估正确的表达式,如果它不再影响逻辑运算符的结果。
在此示例中,这意味着 false && true
的右侧表达式 (true
) 将被忽略(因为 || true
不是该表达式的一部分)
所以在执行中,会发生这样的事情:
将首先评估逻辑运算符&&
在&&
两边有2个表达式,需要求值
第一个表达式 (false
) 的计算结果为 false
第二个表达式 (true
) 是短路的,因为无论结果如何,逻辑运算符都不会产生除false
之外的任何结果
false && true
已评估为 false
接下来将评估逻辑运算符||
(false && true
的结果是false
,因此是false || true
)
第一个表达式 (false
) 的计算结果为 false
第二个表达式 (true
) 的计算结果为 true
false || true
已被评估为真。
最终结果:true
【讨论】:
【参考方案8】:您可能遗漏了运算符仅考虑运算符每一侧的 single 参数。
来自:false && true || true
与false
比较时,仅考虑第一个true
。
所以这个计算结果为false || true
,最终计算结果为true
MDN article 的“任何东西”部分在您的情况下可能有点误导。
【讨论】:
【参考方案9】:this 计算结果为 true 的原因是 OR 的优先级高于 AND,因此等效语句为:
(false && true) || true
这是一个重言式,因为false || true
将始终评估为真。
【讨论】:
【参考方案10】:假布尔 = 0;
真正的布尔值 = 1;
“&&”运算符=乘法运算;
“||” = 添加;
好吗?所以,如果你有这个:
if(true && false || true)
等于:
((1 x 0) + 1)
【讨论】:
【参考方案11】:您的解释是不正确的,您是正确的,运算符优先级是它评估为真的原因。您缺少的是抽象语法树上下文中anything
的含义。
由于操作的顺序,你最终得到了一棵树
||
/ \
&& true
/ \
false anything
anything
的范围,不包括树右侧的 true。
你可以看到some notes from Ohio State on ASTs here。它们适用于所有语言。
【讨论】:
您说得对,但是,自从发布此问题后,我已经编辑了 MDN 文档,以在右侧表达式周围包含括号。了解这一点可能会为您澄清事情。【参考方案12】:“false && anything”中的“false”和“anything”绝对是指 Javascript 表达式/值(如 @Erbureth 的回答中所示),而不是您的源代码。下面是证明这个说法的例子:
“短路”的Javascript代码(每行一个)
var a=false; a && 3
!1 && alert("Never!")
(1==2) && "x"
不会“短路”的Javascript代码(每行一个)
<script>false && </script><p>Other page content...</p></body></html>
false && * x^.++@; true
"false && true"
希望这些空洞的示例有助于阐明为什么将您引用的规则应用于源代码毫无意义。该规则旨在应用于解析源代码后产生的值/子树(此时已应用操作顺序)
【讨论】:
我其实很喜欢这些例子。似乎他们花了一些力气才有点过于复杂,特别是:false && * x^.++@; true
(WTF?)以上是关于为啥`false && true ||真`评估为真?的主要内容,如果未能解决你的问题,请参考以下文章