Operador coalescente nulo en JavaScript con ECMAScript 2020

En esta guía, veremos el operador nulo/operador coalescente nulo agregado a JavaScript con ECMAScript 2020 y cómo podemos utilizarlo para escribir código más limpio, con algunas advertencias.

Introducción

Cuando se trabaja en un ciclo de vida de solicitud-respuesta, desea asegurarse de que llegue una respuesta con el cuerpo deseado, o al menos una respuesta informativa, para que el cliente que solicitó los datos permanezca informado. En el caso de valores nulos, probablemente querrá devolver un resultado diferente.

JavaScript se aseguró de que esto se pueda manejar con su operador nulo, también conocido como Operador coalescente nulo, que se agregó al lenguaje con ECMAScript 2020. Con él, puede devolver un valor o asignarlo a algún otro valor, dependiendo de una expresión booleana.

Esto se puede usar para devolver valores predeterminados si falta otro valor, o para devolver valores diferentes basados ​​en cualquier otra expresión booleana.

El Operador coalescente nulo pertenece al grupo de operadores lógicos de cortocircuito, que veremos en un momento.

Es algo similar al Operador condicional, que a veces se conoce como Operador ternario:

1
2
3
4
5
6
7
// 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

En esta guía, veremos el operador coalescente nulo/nulo, el cortocircuito y los valores verdadero y falso, a través de ejemplos prácticos.

¿Qué es un cortocircuito? {#lo que está en cortocircuito}

JavaScript evalúa los operadores de izquierda a derecha. Con operadores exclusivos donde necesitamos ambos valores para evaluar como verdaderos, a veces es suficiente verificar solo la primera declaración de evaluación.

Si el lado izquierdo del operador es “falso”, independientemente del lado derecho, el operador da como resultado “falso”, por lo que no tiene sentido evaluar el otro lado, y se omite para preservar el poder computacional.

Este proceso se llama cortocircuito.

Antes de ES2020, JavaScript solo tenía dos operadores lógicos de cortocircuito:

  • Operador lógico Y - &&
  • Operador lógico O - ||

Con la nueva actualización, JavaScript introdujo otra:

  • Operador Null Coalescing - ??

El propio cortocircuito hace que el código sea más eficiente porque es necesario realizar menos evaluaciones, aunque, a través del operador Null Coalescing, también podemos modificar la lógica del código en función de una operación de cortocircuito.

Por ejemplo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
let x = 10;
let y = 20;

let result;

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

console.log(result);

Este código dará como resultado:

1
x+y is greater than 20

Sin embargo, también podríamos deshacernos por completo de la declaración ‘si’ y usar un operador AND de cortocircuito en su lugar para acortar las declaraciones:

1
2
3
4
let x = 10;
let y = 20;

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

(x+y) > 20 se evalúa como verdadero, por lo que se ingresa el siguiente bloque y se imprime el mensaje.

¿Dónde está el cortocircuito aquí?

Si (x+y) > 20 fuera falso (hablaremos de esto en un segundo), el intérprete de JavaScript ni siquiera vería la segunda parte de la expresión, y el bloque nunca correr.

De manera similar, podemos usar el operador lógico OR de manera similar:

1
2
3
4
let x = 10;
let y = 20;

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

Naturalmente, esto resulta en:

1
x+y is lesser than 0

Estos ejemplos son bastante simples, pero los operadores lógicos pueden resolver muchos problemas prácticos y hacer que su código sea mucho más limpio. Sin embargo, si no se usa correctamente, puede causarle dolor de cabeza.

Valores verdaderos y falsos

Cuando trabaje con operadores lógicos, encontrará los términos valor verdadero y valor falso.

Un valor de verdad es cualquier valor que no es:

  • nulo
  • indefinido
  • 0
  • '' - cadena vacía
  • falso
  • NaN - No es un número

Todos estos valores se consideran falsos.

En JavaScript, todos los valores falsos se evalúan como “falsos” cuando se usan en bucles o declaraciones “si” y todos los valores verdaderos se evalúan como “verdaderos”.

Ahora que hemos cubierto los requisitos previos, finalmente echemos un vistazo al Operador nulo.

Operador coalescente nulo

El operador nulo comprueba si el valor es nulo, no falso. Un valor nulo es indefinido o nulo.

Esto nos permite asignar valores predeterminados no nulos a las variables cuando no estamos seguros de los resultados.

Por ejemplo, simulemos un servicio de usuario que recupera un usuario de una base de datos y devuelve su nombre. Hay un 10% de posibilidades de que el usuario no exista, lo cual está regulado por una llamada Math.random(). En 10 llamadas a la base de datos, es probable que veamos al menos un usuario perdido, indicado por un resultado nulo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
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');
  }
}

El código generará el nombre o un mensaje que denota la falta de existencia del usuario en la base de datos, aunque esto requiere que realicemos comprobaciones nulas en la declaración if.

Esto resulta en:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
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

La casualidad parece extraña en esta ejecución: ¡3 resultados “nulos”! Este código funciona bien y maneja los casos nulo si la base de datos devuelve un valor nulo.

Alternativamente, podríamos haber usado algo mucho más conciso:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
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)
}

Usando un operador condicional, hemos devuelto null o John Doe con su ID respectivo. Si Math.random() > 0.9, se devuelve null, por lo que hay un 10% de probabilidad de que se devuelva en cada llamada a la base de datos.

Luego, asumiendo posibles valores nulos, hemos usado el Operador nulo para asignar un nuevo valor al resultado del usuario si resulta ser nulo. Si db.find() devuelve null, el valor de la derecha se activa y se devuelve en su lugar.

Este código es mucho más limpio y corto que las sentencias if-else, y las sentencias if-else tienen una mayor tendencia a caer en cascada y volverse aún más complejas. Sin embargo, encadenar múltiples operadores de cortocircuito también es difícil de leer, por lo que no es realmente recomendable reemplazar todos los bloques if-else:

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

console.log(s1)

¿Sabes lo que esto imprimiría? Teniendo en cuenta las posibilidades aleatorias, es probable que le lleve un tiempo analizar esto manualmente y ver qué valores puede anticipar. Este código tiende a dar como resultado 1 con null y 4 en algunos casos, ya que el valor predeterminado devuelto para el operador nulo en este caso es otro nulo. Por lo general, no usará más de uno o dos operadores en casos como estos.

Conclusión

Para concluir, el único propósito del operador nulo es permitirle devolver siempre algún tipo de valor predeterminado, en lugar de devolver algo que no existe, que, en JavaScript, es lo que null y ` media indefinida.