JavaScript中是否有“null coalescing”运算符?

1562

JavaScript 中是否有空值合并运算符(null coalescing operator)?

例如,在 C# 中,我可以这样写:

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

我认为在JavaScript中最好的逼近方法是使用条件运算符:

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

我认为那有点恶心。我能做得更好吗?


39
2018年的注释:x ?? y 语法现在处于一阶段提案状态 - 空值合并 - Aprillion
4
现在有一个Babel插件,可以使用这个确切的语法。 - Jonathan Sudiaman
11
2019年的注释:现在处于第三阶段状态! - Daniel Schaffer
2
2019年后期的注释:空值合并运算符在TypeScript 3.7中可用! - Daniel Schaffer
5
2020年1月的说明:空值合并运算符(nulish coalescing operator)已经可以在Firefox 72中原生支持,但是可选链操作符(optional chaining operator)仍未支持。 - Kir Kanos
6
空值合并运算符(x ?? y)和可选链式操作符(user.address?.street)现在都达到了第四阶段。这意味着什么呢?请看这篇文章的详细描述:https://2ality.com/2015/11/tc39-process.html#stage-4%3A-finished 。 - Mass Dot Net
19个回答

2316

更新

现在JavaScript支持nullish coalescing操作符(??)。当左操作数为nullundefined时,该操作符返回其右操作数;否则返回左操作数。

旧回答

请在使用前检查兼容性。


C#中的null合并运算符??在JavaScript中等价于逻辑或运算符||

var whatIWant = someString || "Cookies!";

有些情况(在下文中进行了澄清)会导致行为与C#不匹配,但这是JavaScript中赋默认值/备选值的一般简洁方式。


澄清

无论第一个操作数的类型是什么,如果将其转换为布尔值的结果为false,则赋值将使用第二个操作数。请注意以下所有情况:

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

这意味着:

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"

56
像“false”、“undefined”、“null”、“0”、“empty”、“deleted”这样的字符串都是真的,因为它们都是非空字符串。 - some
111
值得注意的是,|| 返回第一个“真值”或最后一个“假值”(如果没有值为真),而 && 的工作方式相反:它返回最后一个“真值”或第一个“假值”。 - Justin Johnson
2
@JustinJohnson 提出了一个很好的观点。这个答案比较了三个运算符:?? vs || vs && - Inigo

82
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

这个解决方案的工作方式类似于SQL中的coalesce函数,它接受任意数量的参数,并在没有值的情况下返回null。它的行为类似于C#中的??运算符,即"",false和0被视为非NULL,并因此被视为实际值。如果您来自.NET背景,则这将是最自然的解决方案。


16
非常抱歉这么晚才补充,但我只是想为了完整性而指出这个解决方案有一个缺陷,它没有短路求值;如果你的参数是函数调用,那么它们将会全部被评估,而不管它们的值是否被返回,这与逻辑或运算符的行为不同,因此值得注意。 - Haravikk

75

是的,它即将来临。请查看此处提议此处实现状态

它看起来像这样:

x ?? y

示例

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

47

如果在你的情况下,使用 || 代替 C# 的 ?? 并不能满足要求,因为它会忽略空字符串和零值,你可以编写自己的函数:

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

 var whatIWant = $N(someString, 'Cookies!');

1
alert(null || '') 仍会弹出一个空字符串,我认为我实际上喜欢 alert('' || 'blah') 弹出 blah 而不是一个空字符串 - 这是个好东西!(+1) - Daniel Schaffer
2
我想我实际上更喜欢定义一个返回 false(如果严格为 null/undefined)和 true(否则)的函数 - 将其用于逻辑或; 它可能比许多嵌套的函数调用更易读。例如,$N(a) || $N(b) || $N(c) || d$N($N($N(a, b), c), d) 更易读。 - Bob

17

这里没有人提到NaN的潜在作用,对我来说这也是一种“null-ish”值。因此,我想加入我的意见。

针对给定的代码:

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

如果您使用||运算符,则会获取第一个非false值:

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

如果您使用新的??(null coalescing)运算符,则会得到值为NaN的变量c

vas result = a ?? b ?? c ?? d ?? e ?? f; // result === NaN

对我来说,这两个方法都不正确。在我的合并逻辑的小世界里(可能与你的不同),我认为undefined、null和NaN都是“null-ish”的。因此,我期望从合并方法中得到d(零)。

如果有人的大脑像我一样,并且想要排除NaN,那么这个定制的coalesce方法(与这里发布的方法不同)就可以完成这个任务:

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

如果您希望代码尽可能地短小,并且不介意有些缺乏清晰度,您也可以像@impinball建议的那样使用此代码。 这利用了NaN永远不等于NaN的事实。 您可以在此处阅读更多信息:为什么NaN不等于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;
}

最佳实践 - 将参数视为类似数组的对象,利用 NaN !== NaN(typeof + num.toString() === 'NaN' 是多余的),将当前参数存储在变量中而不是 arguments[i] - Claudia

9

逻辑空值赋值,2020+新解决方案

现在浏览器正在加入一个新的运算符:??=。它将空值合并运算符??和赋值运算符=结合在一起。

注意:在公共浏览器版本中,这种操作符目前还不常见。 如有可用性更改将更新此处说明。

??=检查变量是否为未定义或null,并在已定义时短路。如果没有定义,则将右侧值分配给变量。

基本示例

let a          // undefined
let b = null
let c = false

a ??= true  // true
b ??= true  // true
c ??= true  // false

对象/数组示例

let x = ["foo"]
let y = { foo: "fizz" }

x[0] ??= "bar"  // "foo"
x[1] ??= "bar"  // "bar"

y.foo ??= "buzz"  // "fizz"
y.bar ??= "buzz"  // "buzz"

x  // Array [ "foo", "bar" ]
y  // Object { foo: "fizz", bar: "buzz" }

浏览器支持 - 2022年1月达到89%

Mozilla文档


8

?? vs || vs &&

没有其他答案比较这三个运算符。由于Justin Johnson's comment得票数很高,而double question mark vs && in javascript被标记为重复,因此在回答中包括&&是有意义的。

首先用文字表述,受Justin Johnson评论的启发:

  • ||返回第一个“真值”(truthy)的值,否则返回最后的值,无论它是什么。

  • &&返回第一个“假值”(falsy)的值,否则返回最后的值,无论它是什么。

  • ??返回第一个非null、非undefined的值,否则返回最后的值,无论它是什么。

然后,在实时代码中演示:

let F1,
    F2 = null,
    F3 = 0,
    F4 = '',
    F5 = parseInt('Not a number (NaN)'),
    T1 = 3,
    T2 = 8

console.log( F1 || F2 || F3 || F4 || F5 || T1 || T2 ) // 3 (T1)
console.log( F1 || F2 || F3 || F4 || F5 )             // NaN (F5)

console.log( T1 && T2 && F1 && F2 && F3 && F4 && F5 ) // undefined (F1)
console.log( T1 && T2 )                               // 8 (T2)

console.log( F1 ?? F2 ?? F3 ?? F4 ?? F5 ?? T1 )       // 0 (F3)
console.log( F1 ?? F2)                                // null (F2)


7

是的,它的提案现在处于第四阶段。这意味着该提案已准备好纳入正式的ECMAScript标准中。您已经可以在最新版本的Chrome、Edge和Firefox桌面版中使用它,但我们还需要等待一段时间,直到这个功能达到跨浏览器的稳定性。

请看下面的示例以演示其行为:

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

前面的例子相当于:

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

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

请注意,nullish coalescing不会像||运算符一样处理falsy值(它仅检查undefinednull值),因此以下代码片段将按如下方式执行:

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


对于TypeScript用户,从TypeScript 3.7开始,现在也可以使用此功能。

这太巨大了! - Gerard ONeill

6
阅读了您的澄清后,@Ates Goral的答案提供了如何在JavaScript中执行与您正在进行的操作相同的方法。
@Gumbo的答案提供了检查null的最佳方法;然而,在JavaScript中,重要的是注意 == === 之间的区别,特别是在涉及检查undefined和/或null的问题时。
有一篇关于这两个术语差异的非常好的文章,可以在这里找到。基本上,要理解如果您使用==而不是===,JavaScript将尝试合并您正在比较的值,并返回此合并后比较结果。

6

请注意 JavaScript 中 null 的特定定义。在 JavaScript 中有两个与“无值”相关的定义。 1. Null: 当变量为 null 时,表示其内部没有数据,但该变量已在代码中定义。例如:

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

在这种情况下,您的变量类型实际上是Object。测试一下吧。
window.alert(typeof myEmptyValue); // prints Object
  1. Undefined: when a variable has not been defined before in the code, and as expected, it does not contain any value. like this:

    if ( myUndefinedValue === undefined ) { window.alert('it is undefined'); }
    // alerts
    
如果出现这种情况,你的变量类型是“未定义”。 请注意,如果您使用类型转换比较运算符(==),JavaScript将对这两个空值进行相同的处理。为了区分它们,请始终使用类型严格比较运算符(===)。

2
实际上,null 是一个值。它是 Object 类型的一种特殊值。变量被设置为 null 表示它包含数据,而这个数据指向 null 对象的引用。你的代码中可以定义一个带有 undefined 值的变量。这与变量未声明不同。 - Ates Goral
1
变量声明与未声明的实际区别:alert(window.test)/undefined/; alert("test" in window)/false/; window.test = undefined; alert(window.test)/undefined/; alert("test" in window)/true/; for (var p in window) {/p可以是“test”/} - Ates Goral
2
然而(有点自相矛盾),您可以使用未定义的值var u = undefined;定义一个变量。 - serge
1
@AtesGoral 关于 null 的问题。虽然你说的没错,但是按照惯例,“null”代表“(有用)数据的缺失”。因此,它被认为是“没有数据”。而且,我们不要忘记这是对“null 合并运算符”的问题的回答;在这种情况下,无论内部如何表示,null 都被视为“没有数据”。 - ToolmakerSteve

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接