Czy w JavaScript jest operator „zerowej koalescencji”?

1381

Czy w Javascript jest dostępny zerowy operator koalescencyjny?

Na przykład w języku C # mogę to zrobić:

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

Najlepszym przybliżeniem, jakie mogę znaleźć dla Javascript, jest użycie operatora warunkowego:

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

Co jest trochę obrzydliwe IMHO. Czy mogę zrobić lepiej?

Daniel Schaffer
źródło
31
uwaga z 2018 r .: x ?? yskładnia ma teraz status wniosku 1 etapu - nieważne łączenie
Aprillion
2
Jest teraz wtyczka Babel, która zawiera tę dokładną składnię.
Jonathan Sudiaman
10
Uwaga od 2019 roku: teraz jest status etapu 3!
Daniel Schaffer
3
Uwaga od stycznia 2020: Nullish operator koalescencyjny jest dostępny natywnie w Firefox 72, ale opcjonalny operator łańcuchowy nadal nie jest dostępny.
Kir Kanos
4
Zerowy operator koalescencyjny ( x ?? y) i opcjonalny operator łańcuchowy ( user.address?.street) są teraz zarówno na etapie 4. Oto dobry opis tego, co to znaczy: 2ality.com/2015/11/tc39-process.html#stage-4%3A-finished .
Mass Dot Net

Odpowiedzi:

2133

Aktualizacja

JavaScript obsługuje teraz zerowy operator koalescencji (??) . Zwraca operand po prawej stronie, gdy operand po lewej stronie jest nulllub undefined, a w przeciwnym razie zwraca operand po lewej stronie.

Przed użyciem sprawdź zgodność.


JavaScriptowy odpowiednik operatora koalescencji zerowej C # ( ??) używa logicznej OR ( ||):

var whatIWant = someString || "Cookies!";

Istnieją przypadki (wyjaśnione poniżej), że zachowanie nie będzie pasować do zachowania w C #, ale jest to ogólny, zwięzły sposób przypisywania wartości domyślnych / alternatywnych w JavaScript.


Wyjaśnienie

Bez względu na typ pierwszego operandu, jeśli rzutowanie go na wartość logiczną spowoduje false , przypisanie użyje drugiego operandu. Uważaj na wszystkie poniższe przypadki:

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

To znaczy:

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
źródło
48
Ciągi takie jak „fałsz”, „niezdefiniowany”, „null”, „0”, „pusty”, „usunięty” są prawdziwe, ponieważ nie są ciągami pustymi.
jakieś
4
Trzeba to wyjaśnić. „” nie jest zerowe, ale jest uważane za falsey. Więc jeśli sprawdzasz wartość null i okazuje się, że jest to „”, to nie przejdzie poprawnie tego testu.
ScottKoon,
99
Warto zauważyć, że ||zwraca pierwszą wartość „truey” lub ostatnią „falsey” (jeśli żadna nie może &&dać wartości true) i działa w odwrotny sposób: zwraca ostatnią wartość truey lub pierwszą falsey.
Justin Johnson
19
FYI dla każdego, kogo to nadal obchodzi, 0 i pusty ciąg są oceniane tak samo jak wartości null, jeśli używasz konstruktora typu, aby go zadeklarować. 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 || ''cofnie się, ''jeśli myObj lub myObj.property jest fałszem.
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

to rozwiązanie działa jak funkcja koalescencji SQL, przyjmuje dowolną liczbę argumentów i zwraca wartość null, jeśli żadne z nich nie ma wartości. Zachowuje się jak C # ?? operator w tym sensie, że „”, fałsz i 0 są uważane za NIEZEROWANE i dlatego są liczone jako wartości rzeczywiste. Jeśli pochodzisz z tła .net, będzie to najbardziej naturalne rozwiązanie.

Brent Larsen
źródło
13
Przepraszam za tak późne dodanie, ale chciałem jedynie zauważyć, że to rozwiązanie ma zastrzeżenie, że nie ma oceny zwarcia; jeśli twoje argumenty są wywołaniami funkcji, wszystkie zostaną ocenione niezależnie od tego, czy ich wartość zostanie zwrócona, co różni się od zachowania logicznego operatora OR, więc warto to zauważyć.
Haravikk
63

Tak, już wkrótce. Zobacz propozycję tutaj i status realizacji tutaj .

To wygląda tak:

x ?? y

Przykład

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
źródło
2
Jest teraz w fazie 3 i planowane na następną wersję TypeScript! github.com/microsoft/TypeScript/issues/26578
lautaro.dragan
1
Zerowy operator koalescencyjny ( x ?? y) i opcjonalny operator łańcuchowy ( user.address?.street) są teraz zarówno na etapie 4. Oto dobry opis tego, co to znaczy: 2ality.com/2015/11/tc39-process.html#stage-4%3A-finished .
Mass Dot Net
45

Jeśli ||jako zamiennik C # ??nie jest wystarczająco dobry w twoim przypadku, ponieważ połyka puste ciągi i zera, zawsze możesz napisać własną funkcję:

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

 var whatIWant = $N(someString, 'Cookies!');
coś
źródło
1
alert (null || '') wciąż ostrzega o pustym ciągu i myślę, że tak naprawdę podoba mi się ten alert ('' || 'bla') ostrzega o bla, a nie pusty ciąg - ale warto wiedzieć! (+1)
Daniel Schaffer
1
Myślę, że faktycznie wolałbym zdefiniować funkcję, która zwraca, falsejeśli (ściśle) null / undefined i trueinaczej - używając tego z logicznym lub; może być bardziej czytelny niż wiele wywołań funkcji zagnieżdżonych. np. $N(a) || $N(b) || $N(c) || djest bardziej czytelny niż $N($N($N(a, b), c), d).
Bob
Rozwiązanie Brenta Larsena jest bardziej ogólne
Assimilater,
if .. return .. else ... returnjest idealnym rozwiązaniem dla trójki. return (value === null || value === void 0) ? ifnull : value;
Alex McMillan
15

Nikt tu nie wspomniał o potencjale NaN, który - dla mnie - jest również wartością zerową. Pomyślałem więc, że dodam moje dwa centy.

Dla podanego kodu:

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

Jeśli miałbyś użyć ||operatora, otrzymujesz pierwszą nie-fałszywą wartość:

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

Jeśli użyjesz typowej metody koalescencji, jak tutaj zamieszczono , otrzymasz c, która ma wartość:NaN

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

Żadne z tych nie wydaje mi się właściwe. W moim małym świecie logiki koalescencji, który może różnić się od twojego świata, uważam niezdefiniowane, zerowe i NaN za wszystkie „zerowe”. Oczekiwałbym więc powrotu d(zero) z metody koalescencji.

Jeśli czyjś mózg działa jak mój, a ty chcesz go wykluczyć NaN, ta metoda osiągnie to:

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

Dla tych, którzy chcą, aby kod był jak najkrótszy i nie przeszkadzało mu trochę braku jasności, możesz również użyć tego, jak sugeruje @impinball. Wykorzystuje to fakt, że NaN nigdy nie jest równy NaN. Więcej na ten temat możesz przeczytać tutaj: Dlaczego NaN nie jest równe 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
źródło
Najlepsze praktyki - traktuj argumenty jak tablice, korzystaj z NaN! == NaN ( typeof+ num.toString() === 'NaN'jest nadmiarowy), przechowuj bieżący argument w zmiennej zamiast arguments[i].
Isiah Meadows
@impinball, sugerowana edycja nie działa, zwraca NaN zamiast 0 (zero) z mojego przypadku testowego. Mógłbym technicznie usunąć !== 'number'czek odkąd już ocenione, że nie jest nullani undefined, ale ma to tę zaletę, że każdemu, kto czyta ten kod i stan będzie działać niezależnie od tego, aby bardzo jasne. Twoje pozostałe sugestie nieco skracają kod, więc wykorzystam je.
Kevin Nelson,
2
@impinball, znalazłem twój błąd w sugerowanej edycji, zostawiłeś go jako arg !== arg, ale musisz go mieć arg === arg... to działa. Ma to jednak tę wadę, że jest bardzo niejasne co do tego, co robisz ... wymaga komentarza w kodzie, aby zapobiec usunięciu go przez następną osobę, która przejdzie przez kod i uzna, że arg === argjest zbędna ... ale powiem to tak czy siak.
Kevin Nelson,
Dobry chwyt Nawiasem mówiąc, jest to szybki sposób sprawdzania NaN z wykorzystaniem NaN! == NaN. Jeśli chcesz, możesz to wyjaśnić.
Isiah Meadows
1
sprawdź, czy „nie jest liczbą”, można zastąpić wbudowaną funkcją: isNaN ()
Yury Kozlov,
5

strzeż się specyficznej dla JavaScript definicji wartości null. istnieją dwie definicje „brak wartości” w javascript. 1. Null: gdy zmienna ma wartość null, oznacza to, że nie zawiera w niej danych, ale zmienna jest już zdefiniowana w kodzie. lubię to:

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

w takim przypadku typem twojej zmiennej jest w rzeczywistości Object. Sprawdź to.

window.alert(typeof myEmptyValue); // prints Object
  1. Niezdefiniowany: gdy zmienna nie była wcześniej zdefiniowana w kodzie i zgodnie z oczekiwaniami nie zawiera żadnej wartości. lubię to:

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

w takim przypadku typ zmiennej jest „niezdefiniowany”.

zauważ, że jeśli użyjesz operatora porównującego konwersję typu (==), JavaScript będzie działał jednakowo dla obu tych pustych wartości. aby je rozróżnić, zawsze używaj operatora porównania ścisłego (===).

Farzad
źródło
1
W rzeczywistości null jest wartością. Jest to specjalna wartość typu Object. Zmienna ustawiona na null oznacza, że ​​zawiera dane, przy czym dane są odniesieniem do obiektu null. Zmienna może być zdefiniowana z wartością niezdefiniowaną w kodzie. To nie to samo, co niezadeklarowana zmienna.
Ates Goral
Rzeczywista różnica między deklarowaną lub nie zmienną: alert (window.test) / * undefined * /; alert („test” w oknie) / * false * /; window.test = niezdefiniowany; alert (window.test) / * undefined * /; alert („test” w oknie) / * true * /; for (var p w oknie) {/ * p może być „test” * /}
Ates Goral
1
jednak (nieco paradoksalnie) można zdefiniować zmienną o nieokreślonej wartościvar u = undefined;
Serge
@AtesGoral re null. Podczas gdy to, co mówisz, jest prawdą, zgodnie z konwencją „null” oznacza „brak (użytecznych) danych” . Dlatego uważa się, że jest to „brak danych”. I nie zapominajmy, że jest to odpowiedź na pytanie o „zerowy operator koalescencyjny”; w tym kontekście wartość null jest zdecydowanie traktowana jako „brak danych” - niezależnie od tego, jak jest reprezentowana wewnętrznie.
ToolmakerSteve,
4

Po przeczytaniu twojego wyjaśnienia odpowiedź @Ates Goral zapewnia, jak wykonać tę samą operację, którą wykonujesz w C # w JavaScript.

Odpowiedź @ Gumbo zapewnia najlepszy sposób sprawdzenia, czy nie ma wartości null; ważne jest jednak, aby zauważyć różnicę w ==porównaniu ===z JavaScriptem, szczególnie jeśli chodzi o problemy ze sprawdzaniem undefinedi / lubnull .

Jest to naprawdę dobry artykuł o różnicy w dwóch kategoriach tutaj . Zasadniczo zrozum, że jeśli użyjesz ==zamiast tego ===, JavaScript spróbuje połączyć wartości, które porównujesz i zwróci wynik porównania po tym połączeniu.

Tomek
źródło
Jedną z rzeczy, które mnie wkurzyły w tym artykule (i Jashu) jest niezdefiniowana właściwość window.hello z jakiegoś powodu oceniana na zero. Zamiast tego powinno być niezdefiniowane. Wypróbuj konsolę błędów Firefox i przekonaj się sam.
Ates Goral
3

Zauważ, że create-react-appłańcuch narzędzi React obsługuje koalescencję zerową od wersji 3.3.0 (wydanej 5.12.2019) . Z informacji o wersji:

Opcjonalne operatory łączenia i zerowania

Teraz obsługujemy opcjonalne operatory łączenia i zerowania koalescencji!

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

To powiedziawszy, jeśli używasz create-react-app3.3.0+, możesz już dziś zacząć korzystać z operatora zerowej koalescencji w swoich aplikacjach React.

Lars Blumberg
źródło
3

Tak, a jego propozycja to Etap 4 teraz na . Oznacza to, że wniosek jest gotowy do włączenia do formalnego standardu ECMAScript. Możesz go już używać w najnowszych wersjach Chrome, Edge i Firefox na komputery, ale będziemy musieli poczekać trochę dłużej, aż ta funkcja osiągnie stabilność w różnych przeglądarkach.

Spójrz na następujący przykład, aby zademonstrować swoje zachowanie:

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

Poprzedni przykład jest równoważny z:

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

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

Zauważ, że nullish koalescencyjny będzie nie zagrożeń falsy wartości sposobu ||operator zrobił (nie tylko sprawdza undefinedczy nullwartości), stąd następujący fragment kodu będzie działał w następujący sposób:

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


Dla użytkowników TypesScript, począwszy od TypeScript 3.7 , ta funkcja jest już dostępna.

wierny
źródło
2

Mamy nadzieję, że wkrótce będzie dostępny w Javascript, ponieważ jest w fazie propozycji od kwietnia 2020. Możesz tutaj monitorować stan pod kątem zgodności i wsparcia - https://developer.mozilla.org/en-US/docs/Web/ JavaScript / Reference / Operators / Nullish_coalescing_operator

W przypadku osób korzystających z Typescript możesz użyć zerowego operatora koalescencyjnego z Typescript 3.7

Z dokumentów -

Możesz myśleć o tej funkcji - ??operatorze - jako o „powrocie” do wartości domyślnej w przypadku nulllub undefined. Kiedy piszemy kod podobny

let x = foo ?? bar();

jest to nowy sposób na stwierdzenie, że wartość foozostanie wykorzystana, gdy będzie „obecna”; ale kiedy jest nulllub undefined, obliczyć bar()na jego miejscu.

Vandesh
źródło
0

Dobra odpowiedź

Czy istnieje w JavaScript? Tak. ALE. Obecnie jest na etapie 2020-02-06 na etapie 3 i nie jest jeszcze wszędzie obsługiwany.Kliknij link w poniższym adresie URL i przejdź do nagłówków „Specyfikacje” i „Kompatybilność przeglądarki”, aby uzyskać więcej informacji o tym, gdzie się znajduje.

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

Operator koalescencji zerowej (??) jest operatorem logicznym, który zwraca operand po prawej stronie, gdy operand po lewej stronie jest zerowy lub niezdefiniowany, a w przeciwnym razie zwraca operand po lewej stronie.

W przeciwieństwie do logicznego operatora OR (||), lewy operand jest zwracany, jeśli jest to wartość fałszowania, która nie jest zerowa ani niezdefiniowana. Innymi słowy, jeśli użyjesz || aby podać wartość domyślną innej zmiennej foo, możesz napotkać nieoczekiwane zachowania, jeśli uznasz, że niektóre wartości fałszowania są użyteczne (np. „” lub 0). Zobacz więcej przykładów poniżej.

Chcesz przykłady? Kliknij link, który opublikowałem, ma wszystko.

Karl Morrison
źródło
Dostępne w przeglądarce Chrome 80+ caniuse.com/#feat=mdn-javascript_operators_nullish_coalescing
nathan
Link MDN jest pomocny, ale jest to duplikat odpowiedzi. Zamiast tego powinieneś skomentować odpowiedź vaughana.
Chris
@Chris Jego odpowiedź nie jest wystarczająca, stąd moja odpowiedź.
Karl Morrison
0

Teraz ma pełne wsparcie w najnowszej wersji głównych przeglądarek, takich jak Chrome, Edge, Firefox, Safari itp. Oto porównanie między operatorem zerowym i operatorem Nullish Coalescing

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
źródło