为什么在位运算符可以实现同样功能的情况下要使用逻辑运算符?

12
考虑以下情况:

(true & true & false & false & true) == true //returns: false

正如您所看到的,按位与的行为与逻辑与完全相同:

(true && true && false && false && true) == true //returns: false

当位运算可以达到与逻辑运算相同的效果时,我在想为什么要使用逻辑运算符。

注意:请不要回答这是因为性能问题,因为在Mozilla Firefox中速度要快得多,请参见这个jsPerf:http://jsperf.com/bitwise-logical-and


在第二个例子中,我认为如果第一个值为假,则不会评估其余的值,因为该语句不能为真。 - 最白目
4
你的性能测试存在缺陷。你只测试了truefalse这两个值。当你使用真实函数进行测试时,性能问题就会浮现出来。 - nhahtdh
1
如果你将&替换为+*,你的第一个代码片段仍然有效。但这并不意味着+*总是与布尔运算符&&执行相同的操作。 - georg
1
对一个位进行位运算有点过了 ;) - Caspar Kleijne
做同样的事情?怎么会呢?即使对于数字,它们也会做一些不同的事情。2 && 112 & 10。毫无建设性。 - Salman A
显示剩余3条评论
11个回答

23

短路评估逻辑运算符最常见的用途并不是为了提高性能,而是为了避免出错。参见:

if (a && a.length)

在这里,你不能简单地使用&

请注意,在处理非布尔值时,不能使用&来代替&&。例如,在二进制中,对于数字2(01)和4(10),它们的&结果是0

另外需要注意的是,在if测试之外,&&(就像||)也被用于返回其中一个操作数

"a" & "b" => 0
"a" && "b" => "b"

一般来说,使用&代替&&是可以的。就像在JavaScript代码中省略大部分;一样。但这会迫使你比必要的思考更多(或者会时不时地带来奇怪的错误)。


4
“惰性求值”通常具有比“短路求值”更广泛/不同的含义。 - phant0m
1
@phant0m 这里使用短路更精确,是的。已更新。 - Denys Séguret

13

按位操作的行为相同吗?

不,它们不同。按位运算符作用于整数,而逻辑运算符具有完全不同的语义。只有在使用纯布尔值时,结果可能类似。

  • 按位运算符:评估两个操作数,转换为32位整数,在它们上面进行操作,并返回该数字。
  • 逻辑运算符:评估第一个操作数,如果它是真值/假值,则评估并返回第二个操作数,否则返回第一个结果。这称为短路求值

您已经可以看到结果类型的差异:

(true & true & false & false & true) === 0
(true && true && false && false && true) === false

9

它们并不相同。它们之间的差异在于:

  1. 操作数类型是否会被转换
  2. 是否会对两个操作数都进行评估
  3. 返回值
// sample functions 
function a() { console.log("a()"); return false; }
function b() { console.log("b()"); return true; }

&&(逻辑与)

  1. 检查操作数的真实性。
  2. 使用短路计算,可能不会评估第二个操作数。
  3. 返回最后一个经过评估的操作数,没有类型转换。
a() && b();
// LOG: "a()"
// RET: false

& (按位与)

  1. 临时将操作数转换为32位整数表示(如果必要)
  2. 对两个操作数进行求值
  3. 返回一个数字
a() & b();
// LOG: "a()"
// LOG: "b()"
// RET: 0

1
你可能是想说“按位与”而不是布尔运算。 - phant0m

4
几乎所有的东西都已经被讨论过了,但为了完整起见,我想看一下性能方面的问题(你说这不重要,但它可能非常重要):
JavaScript 有很多难以记住的规则来评估表达式。这包括在涉及更复杂的比较时进行许多类型转换(隐式类型强制转换)。需要通过调用其 toString() 方法将数组和对象转换为字符串,然后将其转换为数字。这导致了巨大的性能损失。
逻辑运算符 && 具有短路特性。这意味着一旦遇到一个虚值(falsy value),就会停止求值,并返回 false。位运算符始终会对整个语句进行求值。
考虑以下(是的,相当极端的)短路示例,当涉及到非常昂贵的操作(将数组和对象转换为字符串)时:(根据 Chromium 90 中 https://jsbench.me 的性能测试结果)
// logical operator
( false && {} && [] ) == true
//  /\ short circuits here
// performance: 805M ops/sec  

// bitwise operator
( false  & {}  & [] ) == true // evaluates the entire statement
// performance: 3.7M ops/sec

你可以看到性能相差了100倍!

1
+1 很好,但基准测试并没有说明性能很重要。在这种情况下,性能大多是一个红鲱鱼,因为语义完全不同。 - phant0m
1
语义主题已经被其他答案涵盖,我只想指出类型强制转换引起的性能问题。 - Christoph
@KyleDelaney 我认为他的意思是两者都必须转换为字符串,然后再转换为数字。如果其中一个没有受到影响,那么举例短路就没有意义,因为你不知道哪个更快。我相当确定他的意思是在这种情况下逻辑运算更快。 - dev7
2
@KyleDelaney 尽管你进行了一次不错的“尸体复活”(这个帖子已经有8年历史了;-),但我为了更清晰地表达我的意思,编辑了我的回答。我希望现在我的意思更清楚了? - Christoph
@Christoph - 干得好!谢谢你迁就我。 - Kyle Delaney
显示剩余2条评论

3
因为使用&&&传达了不同的意图。
第一个表示你正在测试真实性(truthiness)
第二个意味着你在进行一些位运算。在真实的代码中,你将会看到variable1 & variable2。这并不清楚你实际上是想要测试真实性(而不是 truthiness)。代码的读者可能会感到困惑,因为不明显为什么使用了&
此外,考虑到除了bool和函数调用之外的其他值时,语义是完全不同的,正如其他许多帖子所指出的那样。

1
你说得对 - 他们采用了“真值和假值”(尽管这让我个人感到无关紧要的不满;-)),也许你应该链接到一个解释(例如http://11heavens.com/falsy-and-truthy-in-javascript)。我会删除我的原始评论。 - Joris Timmermans
@MadKeithV 好主意,已完成。我也会删除之前的评论。 - phant0m

2
  1. 布尔值允许短路,这可以提高性能或进行安全检查。
  2. 条件语句中使用非布尔值。例如,if ( 1 & 2 )将返回false,而if ( 1 && 2 )将返回true。

我认为你没有理解位运算...尝试使用(2&3)?"true":"false" - Christoph
@Christoph 打扰一下?(2&3)会是true,因为它是0010 & 0011,结果是0010。另一方面,(1&2)0001 & 0010,结果是0000。我的意思是,如果你使用按位&运算符来处理非布尔值,可能会得到意想不到的结果。 - David Kiger
好的,我撤回我的声明,然而句子“在条件语句if中使用非布尔值将返回false”暗示这对所有情况都是正确的,但事实并非如此。也许你想重新表述一下,以便更清楚地表达你的意思。 - Christoph
@Christoph,我想你在“conditional”后面漏了句号,但是意思已经传达了。 :) - David Kiger

1
你不能对位运算符进行短路操作。此外,位运算符可以做更多的事情,而不仅仅是计算布尔表达式。

1

有一个很大的区别:逻辑操作是短路的。这意味着(true && true && false )是最后执行的。这使得强大的构造成为可能,例如使用var myFunc = mozilla.func || opera.sameFunc || webkit.evenOneMoreVariationOfTheSameConcept;的抽象工厂模型。

位运算的所有子表达式都必须被完全评估--顺便说一句,人们很少需要评估常量位或逻辑表达式。


1

首先条件需要先转换第一个和第二个位,然后求和。但是第二个条件会检查逻辑并返回值。

因此,第一个条件比第二个条件慢。

运行此测试:http://jsperf.com/bitwise-logical

在Chrome和IE中,按位运算较慢,但在FireFox中,逻辑运算较慢。


1
对于布尔组合,第一个不一定会更慢。&& 添加了分支,这是很慢的。 - Denys Séguret

0
位运算符(&和|)将两个操作数转换为32位的“整数”,并返回位运算结果。如果操作数不是数字,则其转换为0。
逻辑运算符(&&和||)实际上并非真正的逻辑运算符,而是用于选择其中一个操作数或0。
- 逻辑&&如果两个操作数都存在,则返回第一个操作数,否则返回0。 - 逻辑||返回第一个存在的操作数,否则返回0。 - 如果操作数未定义、为空、为false或为0,则认为该操作数不存在。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接