在JavaScript中使用ECMAScript 2020的空值凝聚运算符

264 阅读5分钟

简介

当在一个_请求-响应生命周期_中工作时,你要确保一个带有所需主体的响应--或者至少是一个信息性的响应到达,这样请求数据的客户端就能保持在循环中。在空值的情况下,你可能想返回一个不同的结果。

JavaScript通过其nullish操作符(也称为空值凝聚操作符)确保了这一点,该操作符是在_ECMAScript 2020_中添加到语言中的。有了它,你既可以返回一个值_,也可以_根据布尔表达式将其分配给其他的值。

这可以用来在另一个值缺失的情况下返回默认值,_或者_根据任何其他布尔表达式返回不同的值。

空值凝聚运算符属于短路逻辑运算符组,我们稍后会看一下。

它与条件运算符有些类似,后者有时也被称为三元运算符

// Null Coalescing Operator //
// If the left-hand side is null, the righthand side is returned
result = nullValue ?? defaultValue

// Conditional operator //
// If the condition is true, the left side is assigned, otherwise, the right side is assigned
condition ? value : value2

在本指南中,我们将通过实际的例子来看看nullish/Null凝聚运算符、短路和真值与假值

什么是 "短路"?

JavaScript从左到右评估运算符。对于排他性运算符,我们需要两个评估为真,有时只检查第一个评估语句就足够了。

如果运算符的左边是false ,不管右边是什么,运算符的结果都是false ,所以评估另一边就没有意义了,为了节省计算能力,会跳过这一边。

这个过程被称为 "短路"。

在ES2020之前,JavaScript只有两个短路的逻辑运算符。

  • 逻辑运算符_AND_-&&
  • 逻辑运算符_OR_-||

在新的更新中,JavaScript又引入了一个。

  • _空值凝聚_运算符 -??

短路本身使代码更有效率,因为需要进行更少的评估,不过,通过Null Coalescing操作符,我们也可以根据短路操作来改变代码逻辑。

比如说。

let x = 10;
let y = 20;

let result;

if(x+y > 20){
	result = "x+y is greater than 20";
}

console.log(result);

这段代码将导致。

x+y is greater than 20

虽然,我们也可以完全抛弃if 语句,而使用短路和运算符来缩短语句。

let x = 10;
let y = 20;

(x+y) > 20 && console.log("x+y is greater than 20");

(x+y) > 20 评估为true ,因此进入下一个块,并打印出信息。

这里哪里短路了?

如果(x+y) > 20 是_虚假_的(我们将在第二部分谈论这个问题),JavaScript解释器甚至不会去看表达式的第二部分,这个块就永远不会运行。

同样的,我们也可以用类似的方式来使用逻辑OR操作符。

let x = 10;
let y = 20;

(x-y) > 0 || console.log("x+y is lesser than 0");

自然,这样做的结果是。

x+y is lesser than 0

这些例子都很简单,然而逻辑运算符可以解决很多实际问题,并使你的代码更加简洁。然而,如果使用不当,你可能会让自己感到头痛。

真值和假值

在使用逻辑运算符时,你会遇到_真值_和_假值_这两个术语。

真值是指任何不属于以下情况的值。

  • null
  • undefined
  • 0
  • '' - 空字符串
  • false
  • NaN - 不是一个数字

所有这些值都被认为是的。

在JavaScript中,当用于循环或if 语句时,所有的_假值_都被评估为false ,所有的_真值_都被评估为true

现在我们已经掌握了先决条件,最后让我们来看看Nullish操作符

归零运算符

Nullish运算符检查值是否为nullish,而不是falsy。一个偏空的值undefinednull

这允许我们在不确定结果的时候给变量分配默认的非空值。

例如,让我们模拟一个用户服务,从数据库中检索一个用户并返回其姓名。有10%的几率该用户不存在,这可以通过调用Math.random() 来调节。在对数据库的10次调用中,我们可能会看到至少一个失踪的用户--用一个null 的结果表示。

class DbConnection {
  find(id) {
    if(Math.random() > 0.9) {
      return null;
    } else {
      return `John Doe ${id}`;
    }
  }
}

const db = new DbConnection();

for (let i = 0; i < 10; i++) {
  let user = db.find(i);

  if(user != null){
	  console.log(user);
  } else {
    console.log('User not found');
  }
}

这段代码将输出用户的名字或一条信息,表示该用户在数据库中不存在,尽管这需要我们在if 语句中进行空检查。

这样的结果是。

John Doe 0
John Doe 1
User not found
John Doe 3
John Doe 4
John Doe 5
User not found
User not found
John Doe 8
John Doe 9

Happenchance在这次运行中似乎很奇怪--3个null 的结果!这段代码工作得很好,如果数据库返回一个null 的值,就可以处理null 的情况。

另外,我们还可以使用更简洁的方法。

class DbConnection {
  find(id) {
    return Math.random() > 0.9 ? null : `John Doe ${id}`
  }
}

const db = new DbConnection();

for (let i = 0; i < 10; i++) {
  let user = db.find(i) ?? "User not found"
  console.log(user)
}

使用一个_条件运算符_,我们返回nullJohn Doe 以及它们各自的ID。如果Math.random() > 0.9 ,则返回null ,所以每次调用数据库时有10%的概率会返回。

然后,假设可能的null 值 - 我们使用Nullish 操作符user 结果分配一个新的值,如果它变成了null 。如果db.find() ,返回null ,右边的值就会被返回。

这段代码比if-else 语句要干净和简短得多,而if-else 语句更容易级联,变得更加复杂。虽然,将多个短路运算符串联起来也会变得难以阅读,所以并不建议替换_所有的_ if-else 块。

let s1 = (Math.random() > 0.5 ? 1 : null) ?? (Math.random() > 0.5 ? 4 : (Math.random() > 0.5 ? null : 1))

console.log(s1)

你知道这将打印出什么吗?考虑到随机的可能性,你可能需要花点时间来手动解析这个,看看你能预见到哪些值。这段代码在某些情况下有导致1null4 的倾向,因为在这种情况下nullish运算符返回的默认值是_另一个null_。一般来说,在这样的情况下,你不会使用超过一个或两个运算符。

结论

总结一下,_nullish运算符_的唯一目的是允许你总是返回某种默认值,而不是返回不存在的东西--在JavaScript中,这就是nullundefined 的意思。