原文链接:https://javascript.info/logical-operators,translate with ❤️ by zhangbao.

在 JavaScript 中,包含 3 个逻辑运算符:||()、&&(与)、!(非)。

虽然他们称为“逻辑”,但是可以应用在任何类型上,不单是布尔值,结果也可以是任意类型的。

我们详细看下.

||(或)

“或”运算符用两个垂直的线符号表示:

  1. result = a || b;

在经典编程中,逻辑或仅仅是为了操纵布尔值。如果其中有一个运算数是 true,那么结果为 true,否则结果就是 false。

在 JavaScript 中,操作符有点棘手和强大,但是首先让我们看看布尔值会发生什么。

有四种可能的逻辑组合:

  1. alert( true || true ); // true
  2. alert( false || true ); // true
  3. alert( true || false ); // true
  4. alert( false || false ); // false

我们可以看到,除非两个操作数同时为 false,否则结果都返回 true。

如果一个操作数不是布尔值,那么它就会被转换成布尔值来进行计算。

例如,数值 1 会作为 true 对待,数值 0 则是作为 false 对待:

  1. if (1 || 0) { // works just like if( true || false )
  2. alert( 'truthy!' );
  3. }

多数时间,或 || 会在 if 语句里使用测试给定条件里有满足的判断:

例如:

  1. let hour = 9;
  2. if (hour < 10 || hour > 18) {
  3. alert( '办公室关门了.' );
  4. }

我们可以使用更多的条件判断:

  1. let hour = 12;
  2. let isWeekend = true;
  3. if (hour < 10 || hour > 18 || isWeekend) {
  4. alert( 'The office is closed.' ); // it is the weekend
  5. }

或会查找第一个真值

上面描述的逻辑有些经典。现在让我们引入 JavaScript 的“额外”特性。

扩展算法的工作原理如下。

给定多个或操作数:

  1. result = value1 || value2 || value3;

或 || 操作符会做下列操作:

  • 从左到右计算操作数。

  • 对于每个操作数,将其转换为布尔值。如果结果为 true,则终止并且返回操作数的原始值。

  • 如果处最后一个的所有操作数都已经被计算了(即都是 false),那么返回最后一个操作数。

值是以原始形式返回的,没有转换。

也就是说,或 || 会返回逻辑链上的第一个真值,如果都是假值,则返回最后一个值作为运算结果。

例如:

  1. alert( 1 || 0 ); // 1 (1 是真值)
  2. alert( true || 'no matter what' ); // (true 是真值)
  3. alert( null || 1 ); // 1 (1 是第一个真值)
  4. alert( null || 0 || 1 ); // 1 (第一个真值)
  5. alert( undefined || null || 0 ); // 0 (都是假值,返回最后一个值)

这就引出了一些有趣的用法,与“纯粹的,经典的,只做布尔值的或”相比。

  1. 从一列变量或者表达式里获得第一个真值

假设我们有几个变量,可能包含数据或者 null/undefined。我们需要选择第一个有数据的。

我们可以使用或 ||:

  1. let currentUser = null;
  2. let defaultUser = "John";
  3. let name = currentUser || defaultUser || "unnamed";
  4. alert( name ); // 结果是 "John" – 第一个真值

如果 currentUser 和 defaultUser 都是假值的话,那么结果就会是 “unnamed”。

  1. 短路计算

操作数不仅可以是值,还可以是任意表达式。或操作数从左到右对它们进行计算和判断。当遇到第一个真值时,计算终止,并且返回值。这个过程被称为“短路计算”,因为它从左到右尽可能短。

当第二个参数给出的表达式可以有副作用,这显然是可见的。比如一个变量赋值。

如果我们执行下面代码的话,x 不会被赋值:

  1. let x;
  2. true || (x = 1);
  3. alert(x); // undefined, because (x = 1) not evaluated

赋值是一个简单的例子,可以涉及到其他副作用。

正如我们所看到的,这样的用例是一种“更短的 if 方法”。第一个操作数被转换成布尔值,如果它是假的,那么第二个操作数就会被计算。

大多数情况下,最好使用“常规”if 来保持代码的易于理解,但有时使用或又很方便。

&&(与)

与操作符用 && 表示:

  1. result = a && b;

在传统编程里,与在两个操作数都是真值时返回 true,否则返回 false:

  1. alert( true && true ); // true
  2. alert( false && true ); // false
  3. alert( true && false ); // false
  4. alert( false && false ); // false

一个 if 语句的例子:

  1. let hour = 12;
  2. let minute = 30;
  3. if (hour == 12 && minute == 30) {
  4. alert( 'Time is 12:30' );
  5. }

类似或,与运算符的操作数可以是任意类型的:

  1. if (1 && 0) { // evaluated as true && false
  2. alert( "won't work, because the result is falsy" );
  3. }

与会查找第一个假值

给定多个与操作数:

  1. result = value1 && value2 && value3;

与 && 操作符会做下列操作:

  • 从左到右计算操作数。

  • 对于每个操作数,将其转换为布尔值。如果结果为 false,则终止并且返回操作数的原始值。

  • 如果处最后一个的所有操作数都已经被计算了(即都是 true),那么返回最后一个操作数。

也就是说,或 || 会返回逻辑链上的第一个假值,如果都是真值,则返回最后一个值作为运算结果。

这个规则类似于或,不同点是与返回第一个假值,而或返回第一个真值

例子:

  1. // 如果第一个操作数为真,
  2. // 与返回第二个操作数:
  3. alert( 1 && 0 ); // 0
  4. alert( 1 && 5 ); // 5
  5. // 如果第一个操作数是假值,
  6. // 与就返回它,第二个操作数会被忽略
  7. alert( null && 5 ); // null
  8. alert( 0 && "no matter what" ); // 0

我们还可以连续传递几个值。看看第一个返回的假值:

  1. alert( 1 && 2 && null && 3 ); // null

当所有值都是真值的时候,就返回最后一个值:

  1. alert( 1 && 2 && 3 ); // 3, the last one

⚠️与 && 的优先级比或 || 要高

与 && 的优先级比或 || 要高

因此代码 a && b || c && d 本质上等同于 && 两边的操作数包围在括号里:(a && b) || (c && d)。

类似于或,与 && 操作符有时可以替换 if。

例如:

  1. let x = 1;
  2. (x > 0) && alert( 'Greater than zero!' );

&& 右边部分的操作只有在前面条件满足时,才会执行。也就是仅当 (x > 0) 为 true 时。

所以我们基本上有一个类似的东西:

  1. let x = 1;
  2. if (x > 0) {
  3. alert( 'Greater than zero!' );
  4. }

可以看到,明显 && 形式的写法更加简短。但是 if 更加直观,并且更加可读。

所以使用每个结构来实现它的目的。如果我们要用如果,就使用 if,如果想用与就使用 &&。

!(非)

布尔非运算符用一个惊叹号 ! 来表示。

语法超级简单:

  1. result = !value;

操作员接受一个单一的参数,并执行以下操作:

  1. 将操作数转换成布尔类型:true/false。

  2. 返回相反的值。

例如:

  1. alert( !true ); // false
  2. alert( !0 ); // true

两个非运算符 !! 有时用于将值转换为布尔类型:

  1. alert( !!"non-empty string" ); // true
  2. alert( !!null ); // false

也就是说,第一个不会将值转换成布尔值并返回相反的值,第二个不会反过来。最后,我们有一个简单的值对布尔转换方法。

还有一种更详细的方法来做同样的事情——内置的 Boolean 函数:

  1. alert( Boolean("non-empty string") ); // true
  2. alert( Boolean(null) ); // false

非运算符的优先级是所有位操作里最高的,所以它总是最先执行,也就是在 &&,|| 之前。

(完)