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


Javascript中是否存在空合并运算符?

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

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

我可以为Javascript找出的最佳近似值是使用条件运算符:

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

这有点ick嘿恕我直言。我可以做得更好吗?


968
2018-01-24 18:18


起源


2018年的说明: x ?? y 语法现在处于第1阶段提案状态 - 无效的合并 - Aprillion


答案:


相当于C#null合并运算符的JavaScript(??)正在使用逻辑OR(||):

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"

1568
2018-01-24 18:25



像“false”,“undefined”,“null”,“0”,“empty”,“deleted”这样的字符串都是真的,因为它们是非空字符串。 - some
@Ates Goral:+1和Array.prototype.forEach = Array.prototype.forEach ||功能(......是什么? - Marco Demaio
值得注意的是 || 返回第一个“truey”值或最后一个“falsey”值(如果没有可以评估为true)和那个 && 以相反的方式工作:返回最后一个truey值或第一个falsey值。 - Justin Johnson
对于仍然关心的任何人来说,如果你使用类型的构造函数来声明它,那么0和空字符串的计算方法与空值相同。 var whatIWant = new Number(0) || 42; // is Number {[[PrimitiveValue]]: 0} var whatIWant = new String("") || "a million bucks"; // is String {length: 0, [[PrimitiveValue]]: ""} - Kevin Heidt
@LuisAntonioPestana var value = myObj && myObj.property || '' 将回归 '' 如果myObj或myObj.property是假的。 - Ates Goral


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被认为是NOT NULL,因此计为实际值。如果你来自.net背景,这将是最自然的感觉解决方案。


56
2018-03-08 05:24



我认为这是最准确的解决方案。 - coolscitist
该 for(var i in arguments) 根据,不保证迭代顺序 MDN。也可以看看 为什么JavaScript的For ... In循环不推荐用于数组? 和 为什么在数组迭代中使用“for ... in”这么糟糕?。 - MvG
对这种后期添加表示歉意,但我只想注意完整性,这个解决方案确实有一个警告,即它没有短路评估;如果您的参数是函数调用,那么它们将会 所有 无论是否返回它们的值,都要进行评估,这与逻辑OR运算符的行为不同,因此值得注意。 - Haravikk


如果 || 作为C#的替代品 ?? 在你的情况下不够好,因为它吞下空字符串和零,你总是可以编写自己的函数:

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

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

38
2018-01-24 18:45



alert(null ||'')仍然会提醒一个空字符串,我想我实际上喜欢那个警告(''||'blah')警告blah而不是空字符串 - 很高兴知道! (1) - Daniel Schaffer
我想我可能更喜欢定义一个返回的函数 false if(严格)null / undefined和 true 否则 - 使用逻辑或;它可能比许多嵌套函数调用更具可读性。例如 $N(a) || $N(b) || $N(c) || d比...更具可读性 $N($N($N(a, b), c), d)。 - Bob
Brent Larsen的解决方案更通用 - Assimilater
if .. return .. else ... return 是三元的完美案例。 return (value === null || value === void 0) ? ifnull : value; - Alex McMillan


这里没有人提到潜力 NaN,对我而言,也是一个零值。所以,我以为我会增加两美分。

对于给定的代码:

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

如果你要使用 || 运算符,你得到第一个非假值:

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

如果使用典型的合并方法, 发布在这里, 你会得到 c,具有以下价值: NaN

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

也不 这些似乎对我而言。在我自己的小合并逻辑世界中,可能与你的世界不同,我认为undefined,null和NaN都是“null-ish”。所以,我希望能回来 d (零)来自合并方法。

如果有人的大脑像我一样工作,你想要排除 NaN,那么这个方法将实现:

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

10
2018-05-26 21:07



最佳实践 - 将参数视为类似数组,利用NaN!== NaN(typeof + num.toString() === 'NaN' 是冗余的),将当前参数存储在变量而不是 arguments[i]。 - Isiah Meadows
@impinball,你建议的编辑不起作用,它从我的测试用例中返回NaN而不是0(零)。我可以从技术上删除 !== 'number' 检查,因为我已经评估过它不是 null 要么 undefined,但这样做的好处是对于阅读此代码的任何人都非常清楚,无论顺序如何,条件都能正常工作。你的其他建议会稍微缩短代码,所以我会使用它们。 - Kevin Nelson
@impinball,我在你建议的编辑中发现了你的错误,你把它留作了 arg !== arg,但你需要它 arg === arg......然后才行。然而,这有一个缺点,就是你不知道你在做什么......需要在代码中注释,以防止被下一个通过代码的人删除并认为 arg === arg 是多余的...但无论如何我都会提出来。 - Kevin Nelson
接得好。顺便说一句,这是一种利用NaN!== NaN这一事实检查NaN的快速方法。如果您愿意,您可以解释一下。 - Isiah Meadows
对于它的价值,我同意NaN实际上只是另一个空值。我会更新我的答案,但你已经覆盖了它。 - Brent Larsen


目前没有支持,但JS标准化流程正在努力: https://github.com/tc39/proposal-optional-chaining


6
2017-07-16 21:17





是的,它即将推出。看到 建议在这里 和 这里的实施状态

它看起来像这样:

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

5
2018-05-07 09:32





要小心null的JavaScript特定定义。 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. 未定义:如果之前未在代码中定义变量,并且按预期方式,则不包含任何值。喜欢这个:

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

如果是这种情况,变量的类型是'undefined'。

请注意,如果使用类型转换比较运算符(==),JavaScript将同时对这两个空值起作用。为了区分它们,总是使用type-strict比较运算符(===)。


4
2018-01-24 18:35



实际上,null是一个值。它是Object类型的特殊值。设置为null的变量意味着它包含数据,该数据是对空对象的引用。可以使用代码中未定义的值定义变量。这与未声明的变量不同。 - Ates Goral
声明与否的变量之间的实际差异:alert(window.test)/ * undefined * /;警告(窗口中的“测试”)/ * false * /; window.test = undefined;警报(window.test)/ * *未定义/;警告(窗口中的“测试”)/ * true * /; for(窗口中的var p){/ * p可以是“test”* /} - Ates Goral
然而(有点悖论)你可以 确定 变量与 未定义 值 var u = undefined; - Serge


在阅读完澄清后,@ Ates Goral的回答提供了如何在JavaScript中执行与C#相同的操作。

@Gumbo的答案提供了检查null的最佳方法;但是,重要的是要注意其中的差异 == 与 === 在JavaScript中 特别 当涉及到检查的问题 undefined 和/或 null

关于两个术语的区别,有一篇非常好的文章 这里。基本上,了解如果你使用 == 代替 ===,JavaScript将尝试合并您正在比较的值并返回比较结果  这种融合。


3
2018-01-24 18:23



关于那篇文章(和Jash)的一件事让我感到困惑的是,一个未定义的window.hello属性由于某种原因被评估为null。它应该是未定义的。试试Firefox错误控制台,亲眼看看吧。 - Ates Goral