Existe-t-il un opérateur de «coalescence nulle» en JavaScript?

1381

Existe-t-il un opérateur de coalescence nul en Javascript?

Par exemple, en C #, je peux faire ceci:

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

La meilleure approximation que je puisse trouver pour Javascript utilise l'opérateur conditionnel:

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

Ce qui est sorta icky à mon humble avis. Puis-je faire mieux?

Daniel Schaffer
la source
31
note de 2018: la x ?? ysyntaxe est maintenant dans l'état de proposition de l'étape 1 - coalescence
nulle
2
Il existe maintenant un plugin Babel qui incorpore cette syntaxe exacte.
Jonathan Sudiaman
10
Remarque à partir de 2019: c'est maintenant le stade 3!
Daniel Schaffer
3
Remarque à partir de janvier 2020: L'opérateur de coalescence nul est disponible en natif dans Firefox 72 mais l'opérateur de chaînage optionnel ne l'est toujours pas.
Kir Kanos
4
L'opérateur coalescent nul ( x ?? y) et l'opérateur de chaînage facultatif ( user.address?.street) sont maintenant tous les deux à l'étape 4. Voici une bonne description de ce que cela signifie: 2ality.com/2015/11/tc39-process.html#stage-4%3A-finished .
Mass Dot Net

Réponses:

2136

Mise à jour

JavaScript prend désormais en charge l' opérateur de coalescence nul (??) . Il renvoie son opérande de droite lorsque son opérande de gauche est nullou undefined, et renvoie sinon son opérande de gauche.

Veuillez vérifier la compatibilité avant de l'utiliser.


L'équivalent JavaScript de l'opérateur de coalescence nul C # ( ??) utilise un OU logique ( ||):

var whatIWant = someString || "Cookies!";

Il y a des cas (clarifiés ci-dessous) où le comportement ne correspondra pas à celui de C #, mais c'est la manière générale et laconique d'attribuer des valeurs par défaut / alternatives en JavaScript.


Clarification

Quel que soit le type du premier opérande, si la conversion en un résultat booléen entraîne false, l'affectation utilisera le deuxième opérande. Méfiez-vous de tous les cas ci-dessous:

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

Ça signifie:

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
la source
48
Les chaînes comme "false", "non défini", "null", "0", "vide", "supprimé" sont toutes vraies car ce sont des chaînes non vides.
certains
4
Cela doit être clarifié. "" n'est pas nul mais est considéré comme falsey. Donc, si vous vérifiez une valeur nulle et qu'il se trouve que "" cela ne passera pas correctement ce test.
ScottKoon
99
Il convient de noter que cela ||renvoie la première valeur "truey" ou la dernière valeur "falsey" (si aucune ne peut être évaluée comme vraie) et cela &&fonctionne dans le sens inverse: renvoyer la dernière valeur truey ou la première valeur falsey.
Justin Johnson
19
Pour info à tous ceux qui se soucient encore, le 0 et la chaîne vide étant évalués de la même manière que les null si vous utilisez le constructeur du type pour le déclarer. var whatIWant = new Number(0) || 42; // is Number {[[PrimitiveValue]]: 0} var whatIWant = new String("") || "a million bucks"; // is String {length: 0, [[PrimitiveValue]]: ""}
Kevin Heidt
5
@LuisAntonioPestana var value = myObj && myObj.property || ''retombera ''si myObj ou myObj.property est faux.
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

cette solution fonctionne comme la fonction de fusion SQL, elle accepte n'importe quel nombre d'arguments et retourne null si aucun d'entre eux n'a de valeur. Il se comporte comme le C # ?? dans le sens où "", false et 0 sont considérés comme NON NULS et comptent donc comme des valeurs réelles. Si vous venez d'un arrière-plan .net, ce sera la solution de sensation la plus naturelle.

Brent Larsen
la source
13
Toutes mes excuses pour un ajout aussi tardif, mais je voulais juste noter pour être complet que cette solution a la mise en garde qu'elle n'a pas d'évaluation de court-circuit; si vos arguments sont des appels de fonction, ils seront tous évalués, que leur valeur soit retournée ou non, ce qui diffère du comportement de l'opérateur logique OU, il convient donc de le noter.
Haravikk
63

Oui, ça arrive bientôt. Voir la proposition ici et l' état de mise en œuvre ici .

Cela ressemble à ceci:

x ?? y

Exemple

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
la source
2
C'est maintenant à l'étape 3, et prévu pour la prochaine version de TypeScript! github.com/microsoft/TypeScript/issues/26578
lautaro.dragan
1
L'opérateur coalescent nul ( x ?? y) et l'opérateur de chaînage facultatif ( user.address?.street) sont maintenant tous les deux à l'étape 4. Voici une bonne description de ce que cela signifie: 2ality.com/2015/11/tc39-process.html#stage-4%3A-finished .
Mass Dot Net
45

Si ||le remplacement des C # ??n'est pas suffisant dans votre cas, car il avale des chaînes vides et des zéros, vous pouvez toujours écrire votre propre fonction:

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

 var whatIWant = $N(someString, 'Cookies!');
qch
la source
1
alert (null || '') alerte toujours une chaîne vide, et je pense que j'aime réellement cette alerte ('' || 'blah') alerte blah plutôt qu'une chaîne vide - bon à savoir cependant! (+1)
Daniel Schaffer
1
Je pense que je pourrais en fait préférer définir une fonction qui retourne falsesi (strictement) nulle / non définie et trueautrement - en utilisant cela avec un logique ou; il pourrait être plus lisible que de nombreux appels de fonctions imbriquées. par exemple, $N(a) || $N(b) || $N(c) || dest plus lisible que $N($N($N(a, b), c), d).
Bob
La solution de Brent Larsen est plus générique
Assimilater
if .. return .. else ... returnest le cas parfait pour un ternaire. return (value === null || value === void 0) ? ifnull : value;
Alex McMillan
15

Personne n'a mentionné ici le potentiel pour NaN, qui - pour moi - est également une valeur nulle. J'ai donc pensé ajouter mes deux cents.

Pour le code donné:

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

Si vous deviez utiliser l' ||opérateur, vous obtenez la première valeur non fausse:

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

Si vous utilisez la méthode de fusion standard, telle que publiée ici , vous obtiendrez c, qui a la valeur:NaN

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

Aucun de ces éléments ne me semble juste. Dans mon propre petit monde de logique de fusion, qui peut différer de votre monde, je considère undefined, null et NaN comme étant tous "null-ish". Donc, je m'attendrais à revenir d(zéro) de la méthode de fusion.

Si le cerveau de quelqu'un fonctionne comme le mien et que vous souhaitez l'exclure NaN, cette méthode accomplira cela:

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;
}

Pour ceux qui veulent un code aussi court que possible, et qui ne se soucient pas d'un petit manque de clarté, vous pouvez également l'utiliser comme suggéré par @impinball. Cela profite du fait que NaN n'est jamais égal à NaN. Vous pouvez en savoir plus ici: Pourquoi NaN n'est-il pas égal à 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
la source
Meilleures pratiques - traitez les arguments comme des tableaux, profitez de NaN! == NaN ( typeof+ num.toString() === 'NaN'est redondant), stockez l'argument actuel dans une variable au lieu de arguments[i].
Isiah Meadows
@impinball, la modification suggérée ne fonctionne pas, elle renvoie NaN au lieu de 0 (zéro) de mon test. Je pourrais techniquement supprimer le !== 'number'chèque car j'ai déjà évalué que ce n'était pas nullou undefined, mais cela a l'avantage d'être très clair pour quiconque lit ce code et la condition fonctionnera indépendamment de l'ordre. Vos autres suggestions raccourcissent légèrement le code, je vais donc les utiliser.
Kevin Nelson
2
@impinball, j'ai trouvé votre bogue dans la modification suggérée, vous l'avez laissé tel quel arg !== arg, mais vous en avez besoin arg === arg... alors ça marche. Cependant, cela a l'inconvénient d'être très peu clair sur ce que vous faites ... nécessite un commentaire dans le code pour éviter d'être supprimé par la prochaine personne qui passe par le code et pense que arg === argc'est redondant ... mais je vais le mettre en place en tous cas.
Kevin Nelson
Bonne prise. Et en passant, c'est un moyen rapide de vérifier les NaN en tirant parti du fait que NaN! == NaN. Si vous le souhaitez, vous pouvez l'expliquer.
Isiah Meadows
1
vérifier "pas un nombre" peut être remplacé par une fonction intégrée: isNaN ()
Yury Kozlov
5

méfiez-vous de la définition spécifique JavaScript de null. il existe deux définitions de «aucune valeur» en javascript. 1. Null: lorsqu'une variable est nulle, cela signifie qu'elle ne contient aucune donnée, mais la variable est déjà définie dans le code. comme ça:

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

dans ce cas, le type de votre variable est en fait Object. Essaye-le.

window.alert(typeof myEmptyValue); // prints Object
  1. Non défini: lorsqu'une variable n'a pas été définie auparavant dans le code, et comme prévu, elle ne contient aucune valeur. comme ça:

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

si tel est le cas, le type de votre variable est «non défini».

notez que si vous utilisez l'opérateur de comparaison de conversion de type (==), JavaScript agira de la même manière pour ces deux valeurs vides. pour les distinguer, utilisez toujours l'opérateur de comparaison de type strict (===).

farzad
la source
1
En fait, null est une valeur. Il s'agit d'une valeur spéciale de type Object. Une variable définie sur null signifie qu'elle contient des données, les données étant une référence à l'objet nul. Une variable peut être définie avec une valeur non définie dans votre code. Ce n'est pas la même chose que la variable non déclarée.
Ates Goral, le
Différence réelle entre une variable déclarée ou non: alert (window.test) / * undefined * /; alert ("test" dans la fenêtre) / * false * /; window.test = undefined; alert (window.test) / * undefined * /; alert ("test" dans la fenêtre) / * true * /; pour (var p dans la fenêtre) {/ * p peut être "test" * /}
Ates Goral
1
cependant (un peu paradoxal) vous pouvez définir une variable avec la valeur indéfinievar u = undefined;
Serge
@AtesGoral re null. Alors que ce que vous dites est vrai, par convention , "null" représente "l'absence de données (utiles)" . Par conséquent, il est considéré comme "aucune donnée". Et n'oublions pas qu'il s'agit d'une réponse à une question sur "un opérateur coalescent nul"; dans ce contexte, null est définitivement traité comme "aucune donnée" - quelle que soit la façon dont il est représenté en interne.
ToolmakerSteve
4

Après avoir lu votre clarification, la réponse de @Ates Goral explique comment effectuer la même opération que vous effectuez en C # en JavaScript.

La réponse de @ Gumbo fournit le meilleur moyen de vérifier la nullité; cependant, il est important de noter la différence par ==rapport ===à JavaScript, en particulier en ce qui concerne les problèmes de vérification de undefinedet / ou null.

Il y a un très bon article sur la différence en deux termes ici . Fondamentalement, sachez que si vous utilisez à la ==place de ===, JavaScript va essayer de fusionner les valeurs que vous comparez et de renvoyer le résultat de la comparaison après cette coalescence.

À M
la source
Une chose qui m'a dérangé à propos de cet article (et Jash) est, une propriété window.hello non définie est évaluée à null pour une raison quelconque. Il ne devrait pas être défini à la place. Essayez la console d'erreur Firefox et voyez par vous-même.
Ates Goral le
3

Notez que la create-react-appchaîne d' outils de React prend en charge la fusion nulle depuis la version 3.3.0 (publiée le 5.12.2019) . D'après les notes de version:

Opérateurs de chaînage et de coalescence nuls facultatifs

Nous prenons désormais en charge les opérateurs de chaînage et de coalescence nuls en option!

// 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

Cela dit, si vous utilisez la version create-react-app3.3.0+, vous pouvez commencer à utiliser l'opérateur null-coalesce dès aujourd'hui dans vos applications React.

Lars Blumberg
la source
3

Oui, et sa proposition est maintenant à l' étape 4 . Cela signifie que la proposition est prête à être incluse dans la norme ECMAScript officielle. Vous pouvez déjà l'utiliser dans les versions de bureau récentes de Chrome, Edge et Firefox, mais nous devrons attendre un peu plus longtemps jusqu'à ce que cette fonctionnalité atteigne la stabilité entre les navigateurs.

Jetez un œil à l'exemple suivant pour illustrer son comportement:

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

L'exemple précédent équivaut à:

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

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

Notez que nullish coalescent sera pas menace falsy les valeurs de la façon dont l' ||opérateur a fait (il vérifie uniquement undefinedou nullvaleurs), d' où l'extrait suivant agira comme suit:

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


Pour les utilisateurs de TypesScript, à partir de TypeScript 3.7 , cette fonctionnalité est également disponible maintenant.

fidèle
la source
2

Nous espérons qu'il sera bientôt disponible en Javascript, car il est en phase de proposition à partir d'avril 2020. Vous pouvez surveiller l'état ici pour la compatibilité et le support - https://developer.mozilla.org/en-US/docs/Web/ JavaScript / Référence / Opérateurs / Nullish_coalescing_operator

Pour les personnes utilisant Typescript, vous pouvez utiliser l' opérateur de coalescence nulle de Typescript 3.7

De la documentation -

Vous pouvez considérer cette fonctionnalité - l' ??opérateur - comme un moyen de «revenir» à une valeur par défaut lorsque vous traitez avec nullou undefined. Quand on écrit du code comme

let x = foo ?? bar();

c'est une nouvelle façon de dire que la valeur foosera utilisée lorsqu'elle est «présente»; mais quand c'est nullou undefined, calculez bar()à sa place.

Vandesh
la source
0

Ok une bonne réponse

Existe-t-il en JavaScript? Oui. MAIS. Il est actuellement en date du 2020-02-06 au stade 3 et n'est pas encore pris en charge partout. Suivez le lien dans l'URL ci-dessous et accédez aux en-têtes "Spécifications" et "Compatibilité du navigateur" pour plus d'informations sur l'endroit où il se trouve.

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

L'opérateur coalescent nul (??) est un opérateur logique qui renvoie son opérande de droite lorsque son opérande de gauche est nul ou non défini, et renvoie sinon son opérande de gauche.

Contrairement à l'opérateur logique OU (||), l'opérande de gauche est retourné s'il s'agit d'une valeur falsifiée qui n'est pas nulle ou non définie. En d'autres termes, si vous utilisez || pour fournir une valeur par défaut à une autre variable foo, vous pouvez rencontrer des comportements inattendus si vous considérez certaines valeurs de falsification comme utilisables (par exemple '' ou 0). Voir ci-dessous pour plus d'exemples.

Vous voulez des exemples? Suivez le lien que j'ai posté, il a tout.

Karl Morrison
la source
Le lien MDN est utile mais il s'agit d'une réponse en double. Vous devriez plutôt commenter sous la réponse de vaughan.
Chris
@Chris Sa réponse ne suffit pas, d'où ma réponse.
Karl Morrison
0

Maintenant, il a un support complet dans la dernière version des principaux navigateurs comme Chrome, Edge, Firefox, Safari, etc. Voici la comparaison entre l'opérateur nul et l'opérateur coalescent nul.

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
la source