¿Hay un operador de "fusión nula" en JavaScript?

1381

¿Hay un operador de fusión nulo en Javascript?

Por ejemplo, en C #, puedo hacer esto:

String someString = null;
var whatIWant = someString ?? "Cookies!";

La mejor aproximación que puedo encontrar para Javascript es usar el operador condicional:

var someString = null;
var whatIWant = someString ? someString : 'Cookies!';

Lo cual es algo asqueroso en mi humilde opinión. ¿Puedo hacerlo mejor?

Daniel Schaffer
fuente
31
nota de 2018: la x ?? ysintaxis ahora está en el estado de propuesta de la etapa 1 - fusión conjunta nula
Aprillion
2
Ahora hay un complemento de Babel que incorpora esta sintaxis exacta.
Jonathan Sudiaman
10
Nota de 2019: ¡ahora es el estado de la etapa 3!
Daniel Schaffer
3
Nota de enero de 2020: el operador de fusión nulo está disponible de forma nativa en Firefox 72, pero el operador de encadenamiento opcional aún no lo está.
Kir Kanos
44
El operador de fusión nulo ( x ?? y) y el operador de encadenamiento opcional ( user.address?.street) ahora están en la Etapa 4. Aquí hay una buena descripción sobre lo que eso significa: 2ality.com/2015/11/tc39-process.html#stage-4%3A-finished .
Mass Dot Net

Respuestas:

2136

Actualizar

JavaScript ahora admite el operador de fusión nulo (??) . Devuelve su operando del lado derecho cuando su operando del lado izquierdo es nullo undefined, y de lo contrario devuelve su operando del lado izquierdo.

Por favor, compruebe la compatibilidad antes de usarlo.


El equivalente de JavaScript del operador de fusión nulo C # ( ??) está utilizando un OR lógico ( ||):

var whatIWant = someString || "Cookies!";

Hay casos (aclarados a continuación) en los que el comportamiento no coincidirá con el de C #, pero esta es la forma general y concisa de asignar valores predeterminados / alternativos en JavaScript.


Aclaración

Independientemente del tipo del primer operando, si lo convierte en un resultado booleano false, la asignación utilizará el segundo operando. Tenga cuidado con todos los casos a continuación:

alert(Boolean(null)); // false
alert(Boolean(undefined)); // false
alert(Boolean(0)); // false
alert(Boolean("")); // false
alert(Boolean("false")); // true -- gotcha! :)

Esto significa:

var whatIWant = null || new ShinyObject(); // is a new shiny object
var whatIWant = undefined || "well defined"; // is "well defined"
var whatIWant = 0 || 42; // is 42
var whatIWant = "" || "a million bucks"; // is "a million bucks"
var whatIWant = "false" || "no way"; // is "false"
Ates Goral
fuente
48
Las cadenas como "falso", "indefinido", "nulo", "0", "vacío", "eliminado" son todas verdaderas ya que son cadenas no vacías.
poco el
44
Esto necesita ser aclarado. "" no es nulo pero se considera falsey. Por lo tanto, si está comprobando un valor nulo y resulta "" que no pasará correctamente esta prueba.
ScottKoon
99
Es de destacar que ||devuelve el primer valor "verdadero" o el último "falsey" (si ninguno puede evaluar como verdadero) y eso &&funciona de la manera opuesta: devolver el último valor verdadero o el primer valor falso.
Justin Johnson
19
Para su información a cualquiera que todavía le importe, el 0 y la cadena vacía se evalúan igual que nulos si usa el constructor del tipo para declararlo. var whatIWant = new Number(0) || 42; // is Number {[[PrimitiveValue]]: 0} var whatIWant = new String("") || "a million bucks"; // is String {length: 0, [[PrimitiveValue]]: ""}
Kevin Heidt
55
@LuisAntonioPestana var value = myObj && myObj.property || ''recurrirá a ''si myObj o myObj.property es falso.
Ates Goral
77
function coalesce() {
    var len = arguments.length;
    for (var i=0; i<len; i++) {
        if (arguments[i] !== null && arguments[i] !== undefined) {
            return arguments[i];
        }
    }
    return null;
}

var xyz = {};
xyz.val = coalesce(null, undefined, xyz.val, 5);

// xyz.val now contains 5

Esta solución funciona como la función de fusión de SQL, acepta cualquier número de argumentos y devuelve un valor nulo si ninguno de ellos tiene un valor. Se comporta como el C # ?? operador en el sentido de que "", falso y 0 se consideran NO NULL y, por lo tanto, cuentan como valores reales. Si vienes de un fondo .net, esta será la solución de sensación más natural.

Brent Larsen
fuente
8
El for(var i in arguments)no garantiza orden de iteración, según la MDN . Consulte también ¿Por qué no se recomienda JavaScript para For ... In loop para matrices? y ¿Por qué usar "for ... in" con iteración de matriz es una mala idea? .
MvG
13
Disculpas por una adición tan tardía, pero solo quería señalar para completar que esta solución tiene la advertencia de que no tiene evaluación de cortocircuito; si sus argumentos son llamadas a funciones, todos serán evaluados independientemente de si se devuelve su valor, que difiere del comportamiento del operador lógico OR, por lo que vale la pena señalarlo.
Haravikk
63

Sí, llegará pronto. Vea la propuesta aquí y el estado de implementación aquí .

Se parece a esto:

x ?? y

Ejemplo

const response = {
  settings: {
    nullValue: null,
    height: 400,
    animationDuration: 0,
    headerText: '',
    showSplashScreen: false
  }
};

const undefinedValue = response.settings?.undefinedValue ?? 'some other default'; // result: 'some other default'
const nullValue = response.settings?.nullValue ?? 'some other default'; // result: 'some other default'
const headerText = response.settings?.headerText ?? 'Hello, world!'; // result: ''
const animationDuration = response.settings?.animationDuration ?? 300; // result: 0
const showSplashScreen = response.settings?.showSplashScreen ?? true; // result: false
vaughan
fuente
2
¡Está en la etapa 3 ahora, y planeado para la próxima versión de TypeScript! github.com/microsoft/TypeScript/issues/26578
lautaro.dragan
1
El operador de fusión nulo ( x ?? y) y el operador de encadenamiento opcional ( user.address?.street) ahora están en la Etapa 4. Aquí hay una buena descripción sobre lo que eso significa: 2ality.com/2015/11/tc39-process.html#stage-4%3A-finished .
Mass Dot Net
45

Si, ||como reemplazo de C #, ??no es lo suficientemente bueno en su caso, porque se traga cadenas vacías y ceros, siempre puede escribir su propia función:

 function $N(value, ifnull) {
    if (value === null || value === undefined)
      return ifnull;
    return value;
 }

 var whatIWant = $N(someString, 'Cookies!');
algo
fuente
1
alert (null || '') todavía alerta una cadena vacía, y creo que en realidad me gusta esa alerta ('' || 'blah') alerta blah en lugar de una cadena vacía, ¡aunque es bueno saberlo! (+1)
Daniel Schaffer
1
Creo que en realidad podría preferir definir una función que devuelva falsesi (estrictamente) nulo / indefinido y de lo truecontrario, usar eso con un lógico o; podría ser más legible que muchas llamadas de funciones anidadas. por ejemplo, $N(a) || $N(b) || $N(c) || des más legible que $N($N($N(a, b), c), d).
Bob
La solución de Brent Larsen es más genérica
Asimilater
if .. return .. else ... returnEs el caso perfecto para un ternario. return (value === null || value === void 0) ? ifnull : value;
Alex McMillan
15

Nadie ha mencionado aquí el potencial NaNque, para mí, también es un valor nulo. Entonces, pensé que agregaría mis dos centavos.

Para el código dado:

var a,
    b = null,
    c = parseInt('Not a number'),
    d = 0,
    e = '',
    f = 1
;

Si usara el ||operador, obtendrá el primer valor no falso:

var result = a || b || c || d || e || f; // result === 1

Si usa el método de fusión típico, como se publica aquí , obtendrá c, que tiene el valor:NaN

var result = coalesce(a,b,c,d,e,f); // result.toString() === 'NaN'

Ninguno de estos me parece correcto. En mi pequeño mundo de lógica de fusión, que puede diferir de su mundo, considero que indefinido, nulo y NaN son "nulos". Entonces, esperaría volver d(cero) del método de fusión.

Si el cerebro de alguien funciona como el mío, y desea excluirlo NaN, entonces este método logrará eso:

function coalesce() {
    var i, undefined, arg;

    for( i=0; i < arguments.length; i++ ) {
        arg = arguments[i];
        if( arg !== null && arg !== undefined
            && (typeof arg !== 'number' || arg.toString() !== 'NaN') ) {
            return arg;
        }
    }
    return null;
}

Para aquellos que desean el código lo más breve posible, y no les importa un poco la falta de claridad, también pueden usar esto como lo sugiere @impinball. Esto aprovecha el hecho de que NaN nunca es igual a NaN. Puede leer más sobre eso aquí: ¿Por qué NaN no es igual a NaN?

function coalesce() {
    var i, arg;

    for( i=0; i < arguments.length; i++ ) {
        arg = arguments[i];
        if( arg != null && arg === arg ) { //arg === arg is false for NaN
            return arg;
        }
    }
    return null;
}
Kevin Nelson
fuente
Mejores prácticas: trate los argumentos como una matriz, aproveche NaN! == NaN ( typeof+ num.toString() === 'NaN'es redundante), almacene el argumento actual en variable en lugar de arguments[i].
Isiah Meadows
@impinball, su edición sugerida no funciona, devuelve NaN en lugar de 0 (cero) de mi caso de prueba. Técnicamente podría eliminar el !== 'number'cheque ya que ya he evaluado que no lo es nullo undefined, pero esto tiene la ventaja de ser muy claro para cualquiera que lea este código y la condición funcionará independientemente del orden. Sus otras sugerencias acortan ligeramente el código, así que las usaré.
Kevin Nelson
2
@impinball, encontré tu error en la edición sugerida, lo dejaste como arg !== arg, pero necesitas que esté arg === arg... entonces funciona. Sin embargo, eso tiene la desventaja de no ser muy claro en cuanto a lo que está haciendo ... requiere un comentario en el código para evitar que la próxima persona que lo revise piense que arg === arges redundante ... pero lo pondré de todas formas.
Kevin Nelson
Buena atrapada. Y, por cierto, esa es una forma rápida de verificar NaNs aprovechando el hecho de NaN! == NaN. Si lo desea, puede explicarlo.
Isiah Meadows
1
la verificación de "no es un número" se puede reemplazar con una función integrada: isNaN ()
Yury Kozlov
5

tenga cuidado con la definición específica de JavaScript de nulo. Hay dos definiciones para "sin valor" en JavaScript. 1. Nulo: cuando una variable es nula, significa que no contiene datos, pero la variable ya está definida en el código. Me gusta esto:

var myEmptyValue = 1;
myEmptyValue = null;
if ( myEmptyValue === null ) { window.alert('it is null'); }
// alerts

en tal caso, el tipo de su variable es en realidad Objeto. Pruébalo.

window.alert(typeof myEmptyValue); // prints Object
  1. Indefinido: cuando una variable no se ha definido antes en el código, y como se esperaba, no contiene ningún valor. Me gusta esto:

    if ( myUndefinedValue === undefined ) { window.alert('it is undefined'); }
    // alerts

en ese caso, el tipo de su variable es 'indefinido'.

tenga en cuenta que si utiliza el operador de comparación de conversión de tipos (==), JavaScript actuará igualmente para ambos valores vacíos. para distinguir entre ellos, use siempre el operador de comparación de tipo estricto (===).

farzad
fuente
1
En realidad, nulo es un valor. Es un valor especial de tipo Objeto. Una variable establecida en nulo significa que contiene datos, siendo los datos una referencia al objeto nulo. Se puede definir una variable con un valor indefinido en su código. Esto no es lo mismo que la variable que no se declara.
Ates Goral el
La diferencia real entre una variable declarada o no: alert (window.test) / * undefined * /; alerta ("prueba" en la ventana) / * falso * /; window.test = indefinido; alert (window.test) / * undefined * /; alerta ("prueba" en la ventana) / * verdadero * /; for (var p in window) {/ * p puede ser "test" * /}
Ates Goral
1
sin embargo (un poco paradójico) puede definir una variable con el valor indefinidovar u = undefined;
Serge
@AtesGoral re null. Si bien lo que usted dice es cierto, por convención , "nulo" representa "la ausencia de datos (útiles)" . Por lo tanto, se considera "sin datos". Y no olvidemos que esta es una respuesta a una pregunta sobre "un operador de fusión nulo"; en este contexto, nulo definitivamente se trata como "sin datos", independientemente de cómo se represente internamente.
ToolmakerSteve
4

Después de leer su aclaración, la respuesta de @Ates Goral proporciona cómo realizar la misma operación que está haciendo en C # en JavaScript.

La respuesta de @ Gumbo proporciona la mejor manera de verificar nulo; Sin embargo, es importante tener en cuenta la diferencia en ==comparación ===con JavaScript, especialmente cuando se trata de problemas de comprobación undefinedy / o null.

Hay un muy buen artículo sobre la diferencia de dos términos aquí . Básicamente, comprenda que si usa en ==lugar de ===, JavaScript intentará fusionar los valores que está comparando y devolver el resultado de la comparación después de esta fusión.

Tom
fuente
Una cosa que me molestó sobre ese artículo (y Jash) es que una propiedad indefinida de window.hello se evalúa como nula por alguna razón. Debería ser indefinido en su lugar. Pruébalo Firefox error console y compruébalo por ti mismo.
Ates Goral el
3

Tenga en cuenta que la create-react-appcadena de herramientas de React admite la fusión nula desde la versión 3.3.0 (lanzada el 5.12.2019) . De las notas de la versión:

Operadores opcionales de encadenamiento y fusión nula

¡Ahora admitimos los operadores opcionales de encadenamiento y fusión nula!

// Optional chaining
a?.(); // undefined if `a` is null/undefined
b?.c; // undefined if `b` is null/undefined

// Nullish coalescing
undefined ?? 'some other default'; // result: 'some other default'
null ?? 'some other default'; // result: 'some other default'
'' ?? 'some other default'; // result: ''
0 ?? 300; // result: 0
false ?? true; // result: false

Dicho esto, en caso de que use create-react-app3.3.0+, puede comenzar a usar el operador de fusión nula hoy en sus aplicaciones React.

Lars Blumberg
fuente
3

Sí, y su propuesta es la Etapa 4 ahora. Esto significa que la propuesta está lista para su inclusión en el estándar formal ECMAScript. Ya puede usarlo en versiones de escritorio recientes de Chrome, Edge y Firefox, pero tendremos que esperar un poco más hasta que esta característica alcance la estabilidad entre navegadores.

Eche un vistazo al siguiente ejemplo para demostrar su comportamiento:

// note: this will work only if you're running latest versions of aforementioned browsers
const var1 = undefined;
const var2 = "fallback value";

const result = var1 ?? var2;
console.log(`Nullish coalescing results in: ${result}`);

El ejemplo anterior es equivalente a:

const var1 = undefined;
const var2 = "fallback value";

const result = (var1 !== null && var1 !== undefined) ?
    var1 :
    var2;
console.log(`Nullish coalescing results in: ${result}`);

Tenga en cuenta que coalescencia nullish será no amenaza Falsy valores de la forma en que el ||operador lo hizo (que sólo verifica si hay undefinedo nullvalores), de ahí el siguiente fragmento actuará de la siguiente manera:

// note: this will work only if you're running latest versions of aforementioned browsers
const var1 = ""; // empty string
const var2 = "fallback value";

const result = var1 ?? var2;
console.log(`Nullish coalescing results in: ${result}`);


Para los usuarios de TypeScript , a partir de TypeScript 3.7 , esta característica también está disponible ahora.

fiel
fuente
2

Esperamos que pronto esté disponible en Javascript, ya que está en fase de propuesta a partir de abril de 2020. Puede monitorear el estado aquí para compatibilidad y soporte: https://developer.mozilla.org/en-US/docs/Web/ JavaScript / Referencia / Operadores / Nullish_coalescing_operator

Para las personas que usan Tipos de letra, puede usar el operador de fusión nulo de Tipos de letra 3.7

De los documentos -

Puede pensar en esta característica, el ??operador, como una forma de "retroceder" a un valor predeterminado cuando se trata con nullo undefined. Cuando escribimos código como

let x = foo ?? bar();

esta es una nueva forma de decir que el valor foose usará cuando esté "presente"; pero cuando es nullo undefined, calcula bar()en su lugar.

Vandesh
fuente
0

Ok, una respuesta adecuada

¿Existe en JavaScript? Si lo hace. PERO. Actualmente es a partir del 2020-02-06 en la Etapa 3 y aún no es compatible en todas partes.Siga el enlace en la URL a continuación y vaya a los encabezados "Especificaciones" y "Compatibilidad del navegador" para obtener más información sobre dónde se encuentra.

Cita de: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator

El operador de fusión nulo (??) es un operador lógico que devuelve su operando del lado derecho cuando su operando del lado izquierdo es nulo o indefinido, y de lo contrario devuelve su operando del lado izquierdo.

Contrariamente al operador lógico OR (||), el operando izquierdo se devuelve si es un valor falso que no es nulo o no está definido. En otras palabras, si usa || para proporcionar algún valor predeterminado a otra variable foo, puede encontrar comportamientos inesperados si considera que algunos valores falsos son utilizables (por ejemplo, '' o 0). Ver abajo para más ejemplos.

¿Quieres ejemplos? Sigue el enlace que publiqué, tiene todo.

Karl Morrison
fuente
El enlace MDN es útil, pero esta es una respuesta duplicada. Deberías comentar bajo la respuesta de Vaughan en su lugar.
Chris
@ Chris Su respuesta no es suficiente, de ahí mi respuesta.
Karl Morrison
0

Ahora tiene soporte completo en la última versión de los principales navegadores como Chrome, Edge, Firefox, Safari, etc. Aquí está la comparación entre el operador nulo y el Operador de fusión nula

const response = {
        settings: {
            nullValue: null,
            height: 400,
            animationDuration: 0,
            headerText: '',
            showSplashScreen: false
        }
    };
    /* OR Operator */
    const undefinedValue = response.settings.undefinedValue || 'Default Value'; // 'Default Value'
    const nullValue = response.settings.nullValue || 'Default Value'; // 'Default Value'
    const headerText = response.settings.headerText || 'Hello, world!'; //  'Hello, world!'
    const animationDuration = response.settings.animationDuration || 300; //  300
    const showSplashScreen = response.settings.showSplashScreen || true; //  true
    /* Nullish Coalescing Operator */
    const undefinedValue = response.settings.undefinedValue ?? 'Default Value'; // 'Default Value'
    const nullValue = response.settings.nullValue ?? ''Default Value'; // 'Default Value'
    const headerText = response.settings.headerText ?? 'Hello, world!'; // ''
    const animationDuration = response.settings.animationDuration ?? 300; // 0
    const showSplashScreen = response.settings.showSplashScreen ?? true; //  false
Rajesh Kumar
fuente