Existe um operador "coalescente nulo" em JavaScript?

1381

Existe um operador coalescente nulo em Javascript?

Por exemplo, em C #, eu posso fazer isso:

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

A melhor aproximação que posso descobrir para Javascript é usar o operador condicional:

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

O que é meio IMHO icky. Posso fazer melhor?

Daniel Schaffer
fonte
31
nota de 2018: a x ?? ysintaxe está agora no status da proposta do estágio 1 - coalescência
nula
2
Agora existe um plugin Babel que incorpora essa sintaxe exata.
Jonathan Sudiaman 29/07/19
10
Nota de 2019: agora é o status do estágio 3!
Daniel Schaffer
3
Nota de janeiro de 2020: o operador coalescente nulo está disponível nativamente no Firefox 72, mas o operador de encadeamento opcional ainda não está.
Kir Kanos
4
O operador coalescente nulo ( x ?? y) e o operador de encadeamento opcional ( user.address?.street) agora são o Estágio 4. Aqui está uma boa descrição sobre o que isso significa: 2ality.com/2015/11/tc39-process.html#stage-4%3A-finished .
Mass Dot Net

Respostas:

2135

Atualizar

O JavaScript agora suporta o operador coalescente nulo (??) . Ele retorna seu operando do lado direito quando o operando do lado esquerdo é nullor undefinede, caso contrário, retorna seu operando do lado esquerdo.

Por favor, verifique a compatibilidade antes de usá-lo.


O equivalente em JavaScript do operador coalescente nulo C # ( ??) está usando um OR lógico ( ||):

var whatIWant = someString || "Cookies!";

Existem casos (esclarecidos abaixo) em que o comportamento não corresponde ao do C #, mas esta é a maneira geral e concisa de atribuir valores padrão / alternativos no JavaScript.


Esclarecimento

Independentemente do tipo do primeiro operando, se o converter em um booleano resultar false, a atribuição usará o segundo operando. Cuidado com todos os casos abaixo:

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

Isso 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
fonte
48
Strings como "false", "undefined", "null", "0", "empty", "delete" são todos verdadeiros, pois são strings não vazios.
algum
4
Isso precisa ser esclarecido. "" não é nulo, mas é considerado falsey. Portanto, se você estiver verificando um valor como nulo e for "", ele não será aprovado corretamente neste teste.
ScottKoon
99
É ||digno de nota que retorna o primeiro valor "truey" ou o último "falsey" (se nenhum puder avaliar como true) e &&funciona da maneira oposta: retornando o último valor truey ou o primeiro valor falsey.
Justin Johnson
19
Para sua informação, para quem ainda se importa, a cadeia 0 e a vazia são avaliadas da mesma forma que nulos se você usar o construtor do tipo para declará-lo. var whatIWant = new Number(0) || 42; // is Number {[[PrimitiveValue]]: 0} var whatIWant = new String("") || "a million bucks"; // is String {length: 0, [[PrimitiveValue]]: ""}
precisa
5
O @LuisAntonioPestana var value = myObj && myObj.property || ''voltará a ''se myObj ou myObj.property for 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 solução funciona como a função de coalescência SQL, aceita qualquer número de argumentos e retorna nulo se nenhum deles tiver um valor. Se comporta como o c # ?? operador no sentido de que "", false e 0 são considerados NOT NULL e, portanto, contam como valores reais. Se você tem experiência em .net, esta será a solução mais natural para os sentimentos.

Brent Larsen
fonte
8
O for(var i in arguments)não garante a ordem da iteração, de acordo com o MDN . Consulte também Por que o loop For… In do JavaScript não é recomendado para matrizes? e Por que usar "for ... in" com iteração de matriz é uma péssima idéia? .
MvG 17/08/2015
13
Desculpas por uma adição tão tardia, mas eu só queria observar por completo que esta solução tem a ressalva de que não possui avaliação de curto-circuito; se seus argumentos são chamadas de função, todos eles serão avaliados independentemente de seu valor ser retornado, o que difere do comportamento do operador OR lógico, portanto vale a pena notar.
Haravikk
63

Sim, está chegando em breve. Veja a proposta aqui e o status da implementação aqui .

Se parece com isso:

x ?? y

Exemplo

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
fonte
2
Está no estágio 3 agora e planejado para a próxima versão do TypeScript! github.com/microsoft/TypeScript/issues/26578
lautaro.dragan
1
O operador coalescente nulo ( x ?? y) e o operador de encadeamento opcional ( user.address?.street) agora são o Estágio 4. Aqui está uma boa descrição sobre o que isso significa: 2ality.com/2015/11/tc39-process.html#stage-4%3A-finished .
Mass Dot Net
45

Se, ||como uma substituição de C #, ??não for boa o suficiente no seu caso, porque engole seqüências de caracteres vazias e zeros, você sempre pode escrever sua própria função:

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

 var whatIWant = $N(someString, 'Cookies!');
sth
fonte
1
alert (null || '') ainda alerta uma string vazia, e acho que realmente gosto desse alerta ('' || 'blah') alerta blá em vez de uma string vazia - é bom saber! (+1)
Daniel Schaffer
1
Eu acho que posso realmente preferir definir uma função que retorne falsese (estritamente) nula / indefinida e de trueoutra forma - usando isso com uma lógica ou; poderia ser mais legível do que muitas chamadas de funções aninhadas. por exemplo, $N(a) || $N(b) || $N(c) || dé mais legível que $N($N($N(a, b), c), d).
28413 Bob
A solução de Brent Larsen é mais genérica
Assimilater
if .. return .. else ... returné o caso perfeito para um ternário. return (value === null || value === void 0) ? ifnull : value;
Alex McMillan
15

Ninguém mencionou aqui o potencial para NaN, que - para mim - também é um valor nulo. Então, pensei em adicionar meus dois centavos.

Para o código fornecido:

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

Se você usar o ||operador, obterá o primeiro valor não falso:

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

Se você usar o método típico de coalescência, conforme publicado aqui , obterá co valor:NaN

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

Nenhuma dessas parece correta para mim. No meu pequeno mundo de lógica de coalescência, que pode diferir do seu mundo, considero indefinido, nulo e NaN como sendo todos "nulos". Então, eu esperaria voltar d(zero) do método de coalescência.

Se o cérebro de alguém funcionar como o meu e você quiser excluir NaN, esse método fará isso:

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 aqueles que desejam o código o mais curto possível e não se importam com a falta de clareza, também é possível usá-lo conforme sugerido pelo @impinball. Isso tira proveito do fato de que NaN nunca é igual a NaN. Você pode ler mais sobre isso aqui: Por que NaN não é 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
fonte
Práticas recomendadas - trate os argumentos como em matriz, aproveite o NaN! == NaN ( typeof+ num.toString() === 'NaN'é redundante), armazene o argumento atual na variável em vez de arguments[i].
Isiah Meadows
@impinball, sua edição sugerida não funciona, ela retorna NaN em vez de 0 (zero) do meu caso de teste. Eu poderia tecnicamente remover a !== 'number'verificação, pois já avaliei que não é nullou undefined, mas isso tem a vantagem de ser muito claro para quem lê esse código e a condição funcionará independentemente da ordem. Suas outras sugestões diminuem um pouco o código, então eu as usarei.
Kevin Nelson
2
@impinball, eu encontrei seu bug na sua edição sugerida, você deixou como arg !== arg, mas precisa que seja arg === arg... então funciona. No entanto, isso tem a desvantagem de não ser claro o que você está fazendo ... requer comentário no código para evitar ser removido pela próxima pessoa que passar pelo código e achar que arg === argé redundante ... mas eu vou colocá-lo de qualquer forma.
Kevin Nelson
Boa pegada. E, a propósito, essa é uma maneira rápida de verificar NaNs aproveitando o fato NaN! == NaN. Se você quiser, pode explicar isso.
Isiah Meadows
1
a verificação de "não é um número" pode ser substituída por uma função interna: isNaN ()
Yury Kozlov
5

cuidado com a definição específica de JavaScript de null. existem duas definições para "sem valor" em javascript. 1. Nulo: quando uma variável é nula, significa que não contém dados, mas a variável já está definida no código. como isso:

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

nesse caso, o tipo da sua variável é realmente Object. teste-o.

window.alert(typeof myEmptyValue); // prints Object
  1. Indefinido: quando uma variável não foi definida anteriormente no código e, como esperado, ela não contém nenhum valor. como isso:

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

nesse caso, o tipo da sua variável é 'indefinido'.

observe que, se você usar o operador de comparação de conversão de tipo (==), o JavaScript atuará igualmente para esses dois valores vazios. para distinguir entre eles, sempre use o operador de comparação de tipo estrito (===).

farzad
fonte
1
Na verdade, nulo é um valor. É um valor especial do tipo Objeto. Uma variável que está sendo definida como nula significa que ela contém dados, sendo os dados uma referência ao objeto nulo. Uma variável pode ser definida com o valor indefinido no seu código. Isso não é o mesmo que a variável que não está sendo declarada.
Ates Goral
A diferença real entre uma variável sendo declarada ou não: alert (window.test) / * undefined * /; alerta ("teste" na janela) / * false * /; window.test = indefinido; alerta (window.test) / * undefined * /; alerta ("teste" na janela) / * true * /; para (var p na janela) {/ * p pode ser "test" * /}
Ates Goral
1
no entanto (a paradoxal bit), você pode definir uma variável com o indefinido valorvar u = undefined;
Serge
@AtesGoral re null. Enquanto o que você diz é verdadeiro, por convenção , "nulo" representa "a ausência de dados (úteis)" . Portanto, é considerado "sem dados". E não vamos esquecer que esta é uma resposta a uma pergunta sobre "um operador coalescente nulo"; nesse contexto, nulo é definitivamente tratado como "sem dados" - independentemente de como é representado internamente.
Página
4

Depois de ler seu esclarecimento, a resposta do @Ates Goral fornece como executar a mesma operação que você está fazendo em C # em JavaScript.

A resposta do @ Gumbo fornece a melhor maneira de verificar se há nulo; no entanto, é importante observar a diferença entre ==versus ===no JavaScript, especialmente quando se trata de questões de verificação de undefinede / ou null.

Há um artigo realmente bom sobre a diferença em dois termos aqui . Basicamente, entenda que, se você usar em ==vez de ===, o JavaScript tentará coalescer os valores que você está comparando e retornar o resultado da comparação após essa coalescência.

Tom
fonte
Uma coisa que me incomodou nesse artigo (e no Jash) é uma propriedade window.hello indefinida sendo avaliada como nula por algum motivo. Deve ser indefinido. Experimente o console de erros do Firefox e veja por si mesmo.
Ates Goral
3

Observe que a create-react-appcadeia de ferramentas do React suporta a coalescência nula desde a versão 3.3.0 (versão 5.12.2019) . Nas notas de versão:

Operadores opcionais de encadeamento e coalescência nula

Agora apoiamos os operadores opcionais de encadeamento e coalescência 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

Dito isto, caso você use o create-react-app3.3.0+, você pode começar a usar o operador de coalescência nula já hoje em seus aplicativos React.

Lars Blumberg
fonte
3

Sim, e sua proposta é a Etapa 4 agora. Isso significa que a proposta está pronta para inclusão no padrão formal do ECMAScript. Você já pode usá-lo em versões recentes de desktop do Chrome, Edge e Firefox, mas teremos que esperar um pouco mais até que esse recurso atinja a estabilidade entre navegadores.

Dê uma olhada no exemplo a seguir para demonstrar seu comportamento:

// 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}`);

O exemplo anterior é equivalente a:

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

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

Note-se que coalescente nullish vai não ameaça Falsas valores a forma como o ||operador fez (ele apenas verifica undefinedou nullvalores), daí o seguinte trecho funcionará da seguinte forma:

// 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 usuários do TypesScript, iniciando o TypeScript 3.7 , esse recurso também está disponível agora.

fiel
fonte
2

Esperamos que esteja disponível em breve em Javascript, como está na fase de proposta a partir de abril de 2020. Você pode monitorar o status aqui para compatibilidade e suporte - https://developer.mozilla.org/en-US/docs/Web/ JavaScript / Referência / Operadores / Nullish_coalescing_operator

Para pessoas que usam o Typecript , você pode usar o operador coalescente nulo do Typescript 3.7

Dos documentos -

Você pode pensar nesse recurso - o ??operador - como uma maneira de "retornar" a um valor padrão ao lidar com nullou undefined. Quando escrevemos código como

let x = foo ?? bar();

esta é uma nova maneira de dizer que o valor fooserá usado quando estiver "presente"; mas quando for nullou undefined, calcule bar()em seu lugar.

Vandesh
fonte
0

Ok, uma resposta adequada

Existe em JavaScript? Sim. MAS. Atualmente, é a partir de 2020-02-06 na Etapa 3 e ainda não é suportado em todos os lugares. Siga o link no URL abaixo e vá para os cabeçalhos "Especificações" e "Compatibilidade do navegador" para obter mais informações sobre a localização.

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

O operador coalescente nulo (??) é um operador lógico que retorna seu operando do lado direito quando o operando do lado esquerdo é nulo ou indefinido e, de outro modo, retorna seu operando do lado esquerdo.

Ao contrário do operador lógico OR (||), o operando esquerdo será retornado se for um valor falso que não seja nulo ou indefinido. Em outras palavras, se você usar || Para fornecer algum valor padrão para outra variável foo, você pode encontrar comportamentos inesperados se considerar alguns valores falsos como utilizáveis ​​(por exemplo, '' ou 0). Veja abaixo para mais exemplos.

Quer exemplos? Segue o link que eu postei, tem tudo.

Karl Morrison
fonte
O link MDN é útil, mas esta é uma resposta duplicada. Você deve comentar a resposta de vaughan.
Chris
@ Chris Sua resposta não é suficiente, daí a minha resposta.
Karl Morrison
0

Agora ele tem suporte completo na versão mais recente dos principais navegadores, como Chrome, Edge, Firefox, Safari etc. Aqui está a comparação entre o operador nulo e o Nullish Coalescing Operator

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
fonte