Есть ли в JavaScript оператор «нулевого слияния»?

1381

Есть ли в Javascript пустой оператор слияния?

Например, в C # я могу сделать это:

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

Лучшее приближение, которое я могу выяснить для Javascript, это использование условного оператора:

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

Который является Сорти Ики ИМХО. Могу ли я сделать лучше?

Даниэль Шаффер
источник
31
примечание от 2018 года: x ?? yсинтаксис сейчас находится на этапе 1, статус заявки - слияние нулевое
апрель
2
Теперь есть плагин Babel, который включает этот точный синтаксис.
Джонатан Судиаман,
10
Примечание от 2019 года: сейчас статус 3 этапа!
Даниэль Шаффер
3
Примечание от января 2020 года: нулевой оператор объединения доступен изначально в Firefox 72, но опциональный оператор цепочки все еще недоступен.
Кир Канос
4
Нулевой оператор слияния ( x ?? y) и необязательный оператор цепочки ( user.address?.street) теперь являются 4-й стадией. Вот хорошее описание того, что это значит: 2ality.com/2015/11/tc39-process.html#stage-4%3A-finished .
Mass Dot Net

Ответы:

2136

Обновить

JavaScript теперь поддерживает нулевой оператор объединения (??) . Он возвращает свой правый операнд, когда его левый операнд равен nullили undefined, и в противном случае возвращает свой левый операнд.

Пожалуйста, проверьте совместимость перед использованием.


JavaScript-эквивалент оператора объединения нулей в C # ( ??) использует логическое ИЛИ ( ||):

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"
Ates Goral
источник
48
Строки, такие как «false», «undefined», «null», «0», «empty», «удалено», являются истинными, поскольку они являются непустыми строками.
некоторые
4
Это необходимо уточнить. «» не является нулевым, но считается фальси. Так что, если вы проверяете значение на ноль, и оно оказывается "", оно не будет правильно проходить этот тест.
ScottKoon
99
Следует отметить, что ||возвращается первое значение «truey» или последнее значение «false» (если ни одно из них не может иметь значение true), и это &&работает противоположным образом: возвращает последнее значение truey или первое значение «false».
Джастин Джонсон
19
К сведению всех, кому все равно, 0 и пустая строка оцениваются так же, как и нули, если вы используете конструктор типа для его объявления. var whatIWant = new Number(0) || 42; // is Number {[[PrimitiveValue]]: 0} var whatIWant = new String("") || "a million bucks"; // is String {length: 0, [[PrimitiveValue]]: ""}
Кевин Хайдт,
5
@LuisAntonioPestana var value = myObj && myObj.property || ''вернется к тому, ''что myObj или myObj.property ложные.
Атес Горал
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

это решение работает как функция объединения SQL, оно принимает любое количество аргументов и возвращает ноль, если ни один из них не имеет значения. Ведет себя как C # ?? оператор в том смысле, что "", false и 0 считаются NOT NULL и поэтому считаются фактическими значениями. Если вы пришли из .net фона, это будет самое естественное решение для чувства.

Брент Ларсен
источник
13
Извиняюсь за столь позднее добавление, но я просто хотел бы отметить для полноты, что у этого решения есть предостережение, что оно не имеет оценки короткого замыкания; если ваши аргументы являются вызовами функций, то все они будут оцениваться независимо от того, возвращается ли их значение, что отличается от поведения логического оператора ИЛИ, поэтому стоит отметить.
Харавикк
63

Да, это скоро. Смотрите предложение здесь и статус реализации здесь .

Это выглядит так:

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
Vaughan
источник
2
Сейчас он на третьем этапе и запланирован на следующий выпуск TypeScript! github.com/microsoft/TypeScript/issues/26578
lautaro.dragan
1
Нулевой оператор слияния ( x ?? y) и необязательный оператор цепочки ( user.address?.street) теперь являются 4-й стадией. Вот хорошее описание того, что это значит: 2ality.com/2015/11/tc39-process.html#stage-4%3A-finished .
Mass Dot Net
45

Если в вашем случае ||замена C # ??недостаточно хороша, поскольку она поглощает пустые строки и нули, вы всегда можете написать свою собственную функцию:

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

 var whatIWant = $N(someString, 'Cookies!');
STH
источник
1
alert (null || '') по-прежнему предупреждает пустую строку, и я думаю, что мне действительно нравится, что alert ('' || 'blah') предупреждает бла, а не пустую строку - хотя это полезно знать! (+1)
Даниэль Шаффер
1
Я думаю, что на самом деле я бы предпочел определить функцию, которая возвращает, falseесли (строго) null / undefined и trueиначе - используя это с логическим или; это может быть более читабельным, чем многие вызовы вложенных функций. например $N(a) || $N(b) || $N(c) || d, более читабельно, чем $N($N($N(a, b), c), d).
Боб
Решение Брента Ларсена является более общим
Assimilater
if .. return .. else ... returnэто идеальный случай для троичного. return (value === null || value === void 0) ? ifnull : value;
Алекс Макмиллан
15

Никто не упомянул здесь потенциал для 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 - все это «ноль». Итак, я ожидал бы получить обратно 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;
}
Кевин Нельсон
источник
Лучшие практики - рассматривайте аргументы как массивы, используйте NaN! == NaN ( typeof+ num.toString() === 'NaN'избыточен), сохраняйте текущий аргумент в переменной вместо arguments[i].
Isiah Meadows
@impinball, предложенное вами изменение не работает, оно возвращает NaN вместо 0 (ноль) из моего теста. Я мог бы технически удалить !== 'number'чек, так как уже оценил, что это не так nullили нет undefined, но преимущество в том, что он очень понятен любому, кто читает этот код, и условие будет работать независимо от порядка. Ваши другие предложения немного сокращают код, поэтому я буду их использовать.
Кевин Нельсон
2
@impinball, я нашел вашу ошибку в предложенном вами редактировании, вы оставили ее как arg !== arg, но вам нужно, чтобы это было arg === arg... тогда это работает. Тем не менее, это имеет тот недостаток, что очень непонятно, что вы делаете ... требуется комментарий в коде, чтобы предотвратить его удаление следующим человеком, который просматривает код и считает arg === argизлишним ... но я подниму это тем не мение.
Кевин Нельсон
Хороший улов. И, кстати, это быстрый способ проверки NaN с использованием факта NaN! == NaN. Если вы хотите, вы можете объяснить это.
Isiah Meadows
1
проверку на «не число» можно заменить встроенной функцией: isNaN ()
Юрий Козлов
5

остерегайтесь специфического для 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

в таком случае тип вашей переменной «неопределенный».

обратите внимание, что если вы используете оператор сравнения преобразования типов (==), JavaScript будет действовать одинаково для обоих этих пустых значений. чтобы различать их, всегда используйте оператор сравнения со строгим типом (===).

Farzad
источник
1
На самом деле, ноль это значение. Это специальное значение типа Object. Переменная, имеющая значение null, означает, что она содержит данные, данные являются ссылкой на нулевой объект. Переменная может быть определена со значением undefined в вашем коде. Это не то же самое, что переменная, которая не объявляется.
Атес Горал
Фактическая разница между объявленной переменной или нет: alert (window.test) / * undefined * /; alert («тест» в окне) / * false * /; window.test = не определено; предупреждение (window.test) / * не определен * /; alert («тест» в окне) / * true * /; for (var p в окне) {/ * p может быть "test" * /}
Ates Goral
1
однако (немного парадоксально) вы можете определить переменную с неопределенным значениемvar u = undefined;
Serge
@AtesGoral re null. Хотя то, что вы говорите, правда, по соглашению , «ноль» означает «отсутствие (полезных) данных» . Следовательно, это считается "нет данных". И давайте не будем забывать, что это ответ на вопрос о «нулевом операторе слияния»; в этом контексте null определенно трактуется как «нет данных» - независимо от того, как он представлен внутри.
ToolmakerSteve
4

Прочитав ваше разъяснение, ответ @Ates Goral показывает, как выполнить ту же самую операцию, которую вы выполняете в C # в JavaScript.

Ответ @ Gumbo обеспечивает лучший способ проверить на ноль; тем не менее, важно отметить разницу в ==сравнении ===с JavaScript, особенно когда речь идет о проверке undefinedи / или проверке null.

Там действительно хорошая статья о разнице в двух слагаемых здесь . По сути, следует понимать, что если вы используете ==вместо этого ===, JavaScript попытается объединить сравниваемые значения и вернуть результат сравнения после этого слияния.

Том
источник
Одна вещь, которая беспокоила меня об этой статье (и Jash), это неопределенное свойство window.hello, которое по какой-то причине оценивается как null Это должно быть неопределенным вместо этого. Попробуйте консоль ошибок Firefox и убедитесь сами.
Атес Горал
3

Обратите внимание, что create-react-appцепочка инструментов React поддерживает объединение нулей с версии 3.3.0 (выпущена 5.12.2019) . Из заметок о выпуске:

Опциональные операторы связывания и объединения

Теперь мы поддерживаем опциональные операторы объединения и обнуления!

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

create-react-appТем не менее, если вы используете 3.3.0+, вы можете начать использовать оператор null-coalesce уже сегодня в своих приложениях React.

Ларс Блумберг
источник
3

Да, и его предложение сейчас 4-й этап . Это означает, что предложение готово для включения в официальный стандарт 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}`);

Обратите внимание, что нулевое объединение не будет угрожать ложным значениям так, как это ||делал оператор (он только проверяет наличие undefinedили nullзначения), поэтому следующий фрагмент будет действовать следующим образом:

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


Для пользователей TypesScript, начиная с TypeScript 3.7 , эта функция теперь также доступна.

Фейтфул
источник
2

Мы надеемся, что скоро он будет доступен в Javascript, так как он находится на этапе предложения с апреля 2020 года. Вы можете отслеживать статус здесь для совместимости и поддержки - https://developer.mozilla.org/en-US/docs/Web/ JavaScript / Справочник / Операторы / Nullish_coalescing_operator

Для людей, использующих Typescript, вы можете использовать нулевой оператор объединения из Typescript 3.7.

Из документов -

Вы можете думать об этой функции - ??операторе - как о способе «возврата» к значению по умолчанию при работе с nullили undefined. Когда мы пишем код, как

let x = foo ?? bar();

это новый способ сказать, что значение fooбудет использоваться, когда оно «присутствует»; но когда это nullили undefined, посчитайте bar()на своем месте.

Vandesh
источник
0

Хорошо правильный ответ

Существует ли это в JavaScript? Да, это так. НО. Это в настоящее время по состоянию на 2020-02-06 на этапе 3 и пока не поддерживается везде.Перейдите по ссылке в URL-адресе ниже и перейдите к заголовкам «Спецификации» и «Совместимость браузера» для получения дополнительной информации о том, где он находится.

Цитата: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator

Нулевой оператор объединения (??) - это логический оператор, который возвращает свой правый операнд, когда его левый операнд равен нулю или не определен, и в противном случае возвращает его левый операнд.

В отличие от логического оператора OR (||), левый операнд возвращается, если это ложное значение, которое не является нулевым или неопределенным. Другими словами, если вы используете || чтобы предоставить какое-либо значение по умолчанию другой переменной foo, вы можете столкнуться с неожиданным поведением, если вы считаете некоторые ложные значения пригодными для использования (например, '' или 0). Смотрите ниже для большего количества примеров.

Хотите примеры? Перейдите по ссылке, которую я разместил, там есть все.

Карл Моррисон
источник
Доступно в Chrome 80+ caniuse.com/#feat=mdn-javascript_operators_nullish_coalescing
Натан,
Ссылка MDN полезна, но это дублирующий ответ. Вместо этого вы должны прокомментировать ответ Вона.
Крис
@ Крис Его ответа недостаточно, отсюда и мой ответ.
Карл Моррисон
0

Теперь он имеет полную поддержку в последних версиях основных браузеров, таких как Chrome, Edge, Firefox, Safari и т. Д. Вот сравнение между нулевым оператором и нулевым оператором слияния

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
Раджеш Кумар
источник