Pytanie Co to jest !! (nie jest) operator w JavaScript?


Widziałem kod, który wydaje się używać operatora, którego nie rozpoznaję, w postaci dwóch wykrzykników, takich jak: !!. Czy ktoś może mi powiedzieć, co robi ten operator?

Kontekst, w którym to zobaczyłem, był

this.vertical = vertical !== undefined ? !!vertical : this.vertical;

2330
2018-04-24 08:13


pochodzenie


Zapamiętaj to przez "bang, bang you're boolean" - Gus
Na wszelki wypadek nie rób tego, co tam jest cytowane. Zrobić if(vertical !== undefined) this.vertical = Boolean(vertical); - jest o wiele czystsze i wyraźniejsze, co się dzieje, nie wymaga niepotrzebnego zadania, jest całkowicie standardowe i jest równie szybkie (na obecnych FF i Chrome) jsperf.com/boolean-conversion-speed . - Phil H
!! nie jest operatorem. To tylko! operator dwa razy. - Vivek
Tylko dla zapisu, Boolean(5/0) nie jest taki sam jak !!5/0 - schabluk
@schabluk, dla rekordu, kolejność operacji Jest powodem !!5/0 produkuje Infinity zamiast true, jak produkowane przez Boolean(5/0). !!5/0 jest równa (!!5)/0 -- znany jako true/0 -- z powodu ! operator mający wyższy priorytet niż / operator. Jeśli chcesz Booleanize 5/0 używając podwójnego uderzenia, musisz użyć !!(5/0). - matty


Odpowiedzi:


Coerces oObject do boolean. Jeśli był to falsey (np. 0, null, undefineditp.), będzie false, Inaczej, true.

!oObject  //Inverted boolean
!!oObject //Non inverted boolean so true boolean representation

Więc !! nie jest operatorem, to po prostu ! operator dwa razy.

Przykład "Prawdziwy świat" "Testowa wersja IE":

let isIE8 = false;  
isIE8 = !! navigator.userAgent.match(/MSIE 8.0/);  
console.log(isIE8); // returns true or false 

Jeśli ⇒

console.log(navigator.userAgent.match(/MSIE 8.0/));  
// returns null  

ale jeśli ty ⇒

console.log(!!navigator.userAgent.match(/MSIE 8.0/));  
// returns true or false

2076
2018-04-24 08:18



Konwertuje nonboolean na odwrócony boolean (na przykład,! 5 będzie fałszywe, ponieważ 5 jest wartością inną niż false w JS), a następnie boolean-odwraca to, więc otrzymujesz oryginalną wartość jako boolean (więc !! Mów prawdę). - Chuck
Łatwy sposób opisania tego jest: Boolean (5) === !! 5; Takie samo rzucanie, mniej znaków. - Micah Snyder
Służy do konwersji wartości truey na boolean true, a falsy też false boolean. - thetoolman
@Micah Snyder uważaj, że w JavaScript lepiej jest używać boolowskich prymitywów zamiast tworzyć obiekty, które boxują booleans z nowym Boolean (). Oto przykład, aby zobaczyć różnicę: jsfiddle.net/eekbu - victorvartan
O ile mi wiadomo, ten wzorzec bang-bang nie jest użyteczny wewnątrz instrukcji if (... _, tylko w instrukcji return funkcji, która powinna zwrócić wartość boolean. - rds


To strasznie niejasny sposób na konwersję typu.

! jest NIE. Więc !true jest false, i !false jest true. !0 jest true, i !1 jest false.

Konwertujesz wartość na wartość logiczną, a następnie odwracasz ją, a następnie odwracasz.

// Maximum Obscurity:
val.enabled = !!userId;

// Partial Obscurity:
val.enabled = (userId != 0) ? true : false;

// And finally, much easier to understand:
val.enabled = (userId != 0);

733
2017-09-10 17:28



!! false = false. !! true = true - roosteronacid
(userId == 0) ? false : true; rani mój mózg. - Andy Gaskell
Strasznie niejasne .... pięknie zwięzłe w moich oczach ... - James Westgate
Czy wariant "o wiele łatwiejszy do zrozumienia" jest o wiele łatwiejszy do zrozumienia? Sprawdzenie w stosunku do 0 nie jest faktycznym sprawdzianem przeciwko 0, ale sprawdzenie względem nieco dziwnej listy wartości JavaScript uważa za równe 0. userId ? true : false  wyjaśnia, że ​​trwa konwersja i obsługuje przypadek, w którym wartość userId mogła być jawnie ustawiona undefined - Ben Regenspan
Mój mózg nie ma problemu z dekodowaniem !!var w Boolean(var) .. i !! jest szybszy (mniej instrukcji do przetworzenia) i krótszy niż alternatywy. - adamJLev


!!expr zwraca wartość logiczną (true lub false) w zależności od prawdomówność wyrażenia. Ma to więcej sensu, gdy jest używane dla typów nie-boolowskich. Rozważ te przykłady, szczególnie trzeci przykład:

          !!false === false
           !!true === true

              !!0 === false
!!parseInt("foo") === false // NaN is falsy
              !!1 === true
             !!-1 === true  // -1 is truthy

             !!"" === false // empty string is falsy
          !!"foo" === true  // non-empty string is truthy
        !!"false" === true  // ...even if it contains a falsy value

     !!window.foo === false // undefined is falsy
           !!null === false // null is falsy

             !!{} === true  // an (empty) object is truthy
             !![] === true  // an (empty) array is truthy; PHP programmers beware!

376
2018-05-15 09:06



Warto zauważyć: !!new Boolean(false) // true - Camilo Martin
...Ale również !!Boolean(false) // false - Camilo Martin
new Boolean(false) jest obiektem, a obiekt jest prawdą, nawet jeśli zawiera wartość falsyfikacji! - Salman A
Tak, wiem, ale weź pod uwagę fakt, że większość rodzimych konstruktorów (String, Number, Date, itp.) mają również funkcję wywołania jako funkcje, jednak w tym przypadku wynik jest inny! - Camilo Martin
@SalmanA Mam również nadzieję, że javascript czasami ma sens: D - Camilo Martin


Zaparzyć herbatę:

!! nie jest operatorem. Jest to podwójne użycie ! - który jest logicznym operatorem "nie".


W teorii:

! określa "prawdę" jakiej wartości nie ma:

  • Prawda jest taka, że false nie jest true (dlatego !false wyniki w true)

  • Prawda jest taka, że true nie jest false (dlatego !true wyniki w false)


!! określa "prawdę" jakiej wartości jest nie nie:

  • Prawda jest taka, że true nie jest nie  true (dlatego !!true prowadzi do true)

  • Prawda jest taka, że false nie jest nie  false (dlatego !!false prowadzi do false)


To, co chcemy w porównaniu określić, to "prawda" o wartość odniesienia, a nie wartość wartość samo odniesienie. Istnieje przypadek użycia, w którym możemy chcieć poznać prawdę o wartości, nawet jeśli spodziewamy się, że ta wartość będzie false (lub falsey), lub jeśli spodziewamy się, że wartość nie będzie typu boolean.


W praktyce:

Rozważ zwięzłą funkcję, która wykrywa funkcjonalność funkcji (w tym przypadku zgodność z platformą) w drodze dynamiczne pisanie (alias "kaczka pisania"). Chcemy napisać funkcję, która zwraca true jeśli przeglądarka użytkownika obsługuje HTML5 <audio> element, ale nie chcemy, aby funkcja zgłaszała błąd, jeśli <audio> jest niezdefiniowany; i nie chcemy tego używać try ... catch obsłużyć ewentualne błędy (ponieważ są one brutto); i również nie chcemy używać sprawdzenia wewnątrz funkcji, która nie będzie konsekwentnie ujawniać prawdy o funkcji (na przykład document.createElement('audio') nadal utworzy element o nazwie <audio> nawet jeśli HTML5 <audio> nie jest wspierany).


Oto trzy podejścia:

// this won't tell us anything about HTML5 `<audio>` as a feature
var foo = function(tag, atr) { return document.createElement(tag)[atr]; }

// this won't return true if the feature is detected (although it works just fine)
var bar = function(tag, atr) { return !document.createElement(tag)[atr]; }

// this is the concise, feature-detecting solution we want
var baz = function(tag, atr) { return !!document.createElement(tag)[atr]; }

foo('audio', 'preload'); // returns "auto"
bar('audio', 'preload'); // returns false
baz('audio', 'preload'); // returns true

Każda funkcja przyjmuje argument dla a <tag> i attribute szukać, ale każdy zwraca różne wartości w zależności od tego, co określają porównania.

Ale czekaj, jest więcej!

Niektórzy z was zapewne zauważyli, że w tym konkretnym przykładzie można po prostu sprawdzić za pomocą nieruchomości nieco bardziej wydajny sposób sprawdzenia, czy dany przedmiot ma własność. Można to zrobić na dwa sposoby:

// the native `hasOwnProperty` method
var qux = function(tag, atr) { return document.createElement(tag).hasOwnProperty(atr); }

// the `in` operator
var quux = function(tag, atr) { return atr in document.createElement(tag); }

qux('audio', 'preload');  // returns true
quux('audio', 'preload'); // returns true

Odchodzimy ...

Jakkolwiek rzadkie mogą być te sytuacje, może istnieć kilka scenariuszy, w których najbardziej zwięzłe, najbardziej wydajne, a zatem najbardziej preferowane sposoby uzyskiwania true od wartości nie-boolowskiej, prawdopodobnie nieokreślonej, jest rzeczywiście przy użyciu !!. Miejmy nadzieję, że to śmiesznie wyczyści to.


130
2018-02-22 23:45



totalnie niesamowita odpowiedź, ale nie widzę użyteczności !! zbudować. Od if() oświadczenie już rzuca wyrażenie do wartości logicznej, jawnie rzucanie wartości zwracanej funkcji testowej do wartości logicznej jest zbędne - ponieważ "prawdomówność" === prawda aż do if() oświadczenie i tak idzie. A może brakuje mi scenariusza, w którym POTRZEBUJESZ prawdy, aby rzeczywiście była boolowska? true? - Tom Auger
@TomAuger if() oświadczenia są przeliczane na wartości logsey, ale mówią, że chcesz ustawić flagę boolowską na obiekcie - nie będzie ona rzucać if() oświadczenie ma. Na przykład object.hasTheThing = !!castTheReturnValToBoolNoMatterWhat() też ustawił true lub false zamiast prawdziwej wartości zwracanej. Innym przykładem mogą być wszyscy administratorzy id z 0 a nie-administratorzy to id 1 lub wyżej. Aby dostać truejeśli ktoś nie jest administratorem, możesz to zrobić person.isNotAdmin = !!admin.id. Kilka przypadków użycia, ale jest zwięzła, gdy jest. - Benny Schmidt


!! konwertuje wartość z prawej strony na równoważną wartość logiczną. (Pomyśl o sposobie "odlewania" przez biednego człowieka). Jego zamiar zwykle przekazuje czytelnikowi, że kod nie ma znaczenia co wartość jest w zmiennej, ale co to jest wartość "prawda" jest.


87
2017-09-10 17:26



Lub w przypadku wartości boolowskiej po prawej stronie, nie robi nic. - Daniel A. White
@Daniel: ! nadal odwraca wartość w prawo. W przypadku wartości logicznej, najbardziej prawe ! neguje wartość, podczas gdy lewe najbardziej ! neguje to jeszcze raz. Efektem sieci jest to, że nie ma zmian, ale większość silników generuje kody operacji dla podwójnej negacji. - Crescent Fresh
Ale o co chodzi? Jeśli zrobię if(0){... JavaScript już wie, że jest to fałsz. Dlaczego lepiej powiedzieć if(!!0){...? - CodyBugstein
chodzi o zmienne, których nie znasz; jeśli może to być liczba całkowita lub ciąg znaków, obiekt lub wartość pusta, niezdefiniowana itp. Jest to łatwy sposób na sprawdzenie istnienia. - mix3d


!!foo stosuje jednoargumentowy operator nie dwa razy i jest używany do rzutowania na typ boolowski podobny do użycia unarnego plusa +foo rzutować na liczbę i łączenie pustego ciągu ''+foo rzucać na ciąg.

Zamiast tych hacków można również użyć funkcji konstruktora odpowiadających typom pierwotnym (bez za pomocą new), aby jawnie rzutować wartości, tj

Boolean(foo) === !!foo
Number(foo)  === +foo
String(foo)  === ''+foo

59
2017-09-10 21:15



Ale wtedy możesz napotkać problemy z instanceof. nowy Boolean (1) instanceof Object -> true !! 1 instanceof Object -> false - Seamus
nie, nie możesz: zauważyć, że funkcje konstruktora są wywoływane bez new - jak wyraźnie wspomniano w mojej odpowiedzi - Christoph
fantastyczny! Jest to przydatne przy niewielkim hackowaniu, gdy musisz przetestować ciągi z "0" jako false zamiast z true. (tj. podczas odczytu wartości z selekcji, ponieważ są one odczytywane jako String). Tak więc, jeśli chcesz wziąć pod uwagę "0" jako negatywny (Boolean false), asuming x="0" po prostu zrób: x=!!+x; //false który jest taki sam jak Boolean(Number(x)) Number (lub + x) konwertuje ciąg "0" na 0, który WYDAJE się na false, a następnie Boolean (!! x) rzuca go bezpośrednio do wartości logicznej. Bułka z masłem! - DiegoDD
@DiegoDD dlaczego wybrałbyś !!+x vs x !== "0"? - placeybordeaux
@placeybordeaux, ponieważ na przykład możesz przekonwertować wartość i przypisać ją do innej zmiennej, niezależnie od tego, czy zamierzasz ją porównać z czymś innym, czy nie. - DiegoDD


Tak wiele odpowiedzi robi połowę pracy. Tak, !!X może być odczytany jako "prawdomówność X [reprezentowana jako boolowska]". Ale !! nie jest, praktycznie rzecz biorąc, tak ważna dla ustalenia, czy pojedyncza zmienna jest (lub nawet jeśli wiele zmiennych jest) prawdą czy fałszem. !!myVar === true jest taki sam jak właśnie myVar. Porównywanie !!X na "prawdziwą" wartość boolowską nie jest zbyt użyteczna.

Co zyskujesz !! to możliwość sprawdzenia prawidłowości wielu zmiennych przeciwko sobie w powtarzalny, znormalizowany (i przyjazny dla JSLinta) sposób.

Po prostu casting :(

To jest...

  • 0 === false jest false.
  • !!0 === false jest true.

Powyższe nie jest tak przydatne. if (!0) daje takie same wyniki jak if (!!0 === false). Nie potrafię wymyślić dobrego argumentu za przeniesieniem zmiennej do wartości boolowskiej, a następnie porównaniem z "prawdziwą" wartością logiczną.

Zobacz "== i! =" Od Wskazówki JSLint (uwaga: Crockford porusza trochę swoją stronę, ten link może w pewnym momencie umrzeć), po trosze dlaczego:

Operatory == i! = Wpisują wymuszenie przed porównaniem. Jest to złe, ponieważ powoduje, że "\ t \ r \ n" == 0 jest prawdziwe. Może to maskować błędy typu. JSLint nie może w sposób niezawodny określić, czy == jest używane poprawnie, więc najlepiej nie używać w ogóle == i! = I zawsze używać bardziej wiarygodnych operatorów === i! ==.

Jeśli zależy ci tylko na tym, że wartość jest prawdą lub fałszem, użyj krótkiej formy. Zamiast
(foo != 0)

tylko powiedz
(foo)

i zamiast
(foo == 0)

mówić
(!foo)

Zauważ, że są pewne niezrozumiałe przypadki gdzie wartość boolowska zostanie przeliczona na liczbę (true jest rzutowany na 1 i false do 0) podczas porównywania wartości logicznej z liczbą. W tym przypadku, !! może być przydatna psychicznie. Chociaż znowu są to przypadki, w których porównujesz wartość logiczną nie-boolowską z typową wartością logiczną, co jest, imo, poważnym błędem.  if (-1) to wciąż droga do tego.

╔═══════════════════════════════════════╦═══════════════════╦═══════════╗
║               Original                ║    Equivalent     ║  Result   ║
╠═══════════════════════════════════════╬═══════════════════╬═══════════╣
║ if (-1 == true) console.log("spam")   ║ if (-1 == 1)      ║ undefined ║
║ if (-1 == false) console.log("spam")  ║ if (-1 == 0)      ║ undefined ║
║   Order doesn't matter...             ║                   ║           ║
║ if (true == -1) console.log("spam")   ║ if (1 == -1)      ║ undefined ║
╠═══════════════════════════════════════╬═══════════════════╬═══════════╣
║ if (!!-1 == true) console.log("spam") ║ if (true == true) ║ spam      ║ better
╠═══════════════════════════════════════╬═══════════════════╬═══════════╣
║ if (-1) console.log("spam")           ║ if (truthy)       ║ spam      ║ still best
╚═══════════════════════════════════════╩═══════════════════╩═══════════╝

A rzeczy stają się jeszcze bardziej szalone w zależności od silnika. Na przykład WScript wygrywa nagrodę.

function test()
{
    return (1 === 1);
}
WScript.echo(test());

Z powodu jakiś historyczny jive Windows, to wyjdzie -1 w oknie komunikatu! Wypróbuj go w wierszu polecenia cmd.exe i zobacz! Ale WScript.echo(-1 == test()) nadal daje ci 0 lub WScript false. Odwracać wzrok. To jest ohydne.

Porównywanie prawdy :)

Ale co jeśli mam dwie wartości, które muszę sprawdzić dla równego truthi / falsi-ności?

Udawaj, że mamy myVar1 = 0; i myVar2 = undefined;.

  • myVar1 === myVar2 jest 0 === undefined i jest oczywiście fałszywe.
  • !!myVar1 === !!myVar2 jest !!0 === !!undefined i jest prawdą! Ta sama prawdomówność! (W tym przypadku obie "mają prawdziwość fałszu".)

Tak więc jedynym miejscem, w którym naprawdę potrzebujesz użyć "zmiennych binarnych", byłoby gdybyś miał sytuację, w której sprawdzasz, czy obie zmienne mają podobnie prawdomówność, prawda? To jest, posługiwać się !! jeśli chcesz zobaczyć, czy są dwie vary zarówno prawdą, jak i obiema falsjami (lub nie), to znaczy równy (albo nie) prawdomówność.

Nie mogę wymyślić wspaniałego, nie-wymyślonego przypadku użycia tego offhandu. Może masz "połączone" pola w formularzu?

if (!!customerInput.spouseName !== !!customerInput.spouseAge ) {
    errorObjects.spouse = "Please either enter a valid name AND age " 
        + "for your spouse or leave all spouse fields blank.";
}

Więc teraz, jeśli masz prawdę dla obu lub Fałszywe imię i nazwisko współmałżonka, możesz kontynuować. W przeciwnym razie masz tylko jedno pole z wartością (lub bardzo wcześnie zaaranżowane małżeństwo) i musisz utworzyć dodatkowy błąd na swoim errorObjects kolekcja.


EDYCJA 24 października 2017 r .: 

Biblioteki innych firm, które oczekują jawnych wartości logicznych

Oto interesujący przypadek ... !! może być przydatny, gdy biblioteki stron trzecich oczekują jawnych wartości logicznych.

Na przykład, Fałsz w JSX (React) ma specjalne znaczenie to nie jest wyzwalane na prostych fałszerstwach. Jeśli próbowałeś zwrócić coś takiego w swoim JSX, spodziewając się int messageCount...

{messageCount && <div>You have messages!</div>}

... możesz być zaskoczony, gdy zobaczysz React render a 0 kiedy masz zero wiadomości. Musisz jawnie zwrócić wartość false, aby JSX nie renderował. Powyższe oświadczenie zwraca 0, które JSX szczęśliwie przekazuje, tak jak powinno. Nie może powiedzieć, że tego nie zrobiłeś Count: {messageCount && <div>Get your count to zero!</div>} (lub coś mniej wymyślnego).

  • Jedna poprawka obejmuje bangbang, który wymusza 0 w !!0, który jest false:
    {!!messageCount && <div>You have messages!</div>}

  • Dokumenty JSX "sugerują, że jesteś bardziej konkretny, pisz samopowijający się kod i użyj porównania, aby wymusić na wartość logiczną.
    {messageCount > 0 && <div>You have messages!</div>}

  • Bardziej komfortowo posługuję się falsyfikacją z trójskładnikowym -
    {messageCount ? <div>You have messages!</div> : false}

Weź pod uwagę, że to jest konwencją JSX, a nie jeden związany z JavaScript.

Ale jeśli widzisz coś dziwnego 0s w renderowanym JSX, pomyśl luźne zarządzanie falsjami.


47
2018-04-29 18:14



Dobre wytłumaczenie. Więc powiedziałbyś !! nie jest w tym absolutnie konieczne Wykrywanie funkcji pracownika przykład? if (!!window.Worker) - jk7
Nie, nie potrzebujesz tego. Prawda i true "zewnętrznie" działają dokładnie tak samo w if. Wciąż próbuję, ale nie mogę wymyślić powodu, dla którego wolałbym rzucać prawdę na wartość boolowską poza zakręconym przypadkiem "porównywać z prawdą", powyżej, z wyjątkiem czytelności, jeśli ponownie użyjesz tej wartości później, jak w q przykład biblioteki. Ale nawet wtedy jest to skrót informacyjny i twierdzę, że za każdym razem lepiej oceniasz prawdę. - ruffin
React jest głównym powodem, dla którego zaczęliśmy używać !! wzór, ale zdarza się, że jest to bardzo wygodna i powtarzalna rzecz. Muszę się tylko martwić o prawdziwość lub fałsz czegoś, a nie o rodzaj ukryty. - Alexander Pritchard


To tylko operator logiczny NOT, dwa razy - służy do konwersji czegoś na boolowski, np .:

true === !!10

false === !!0

44
2018-04-24 08:18



Dlaczego miałbyś to zrobić? Użyj ! operator przekonwertować na wartość boolean, a następnie użyć === do porównania typu? Po prostu zaakceptuj, że nie masz bezpieczeństwa typu i wykonaj val> 0 lub coś podobnego. - Darren Clark
@Darren: On nie porównuje typów; on mówi ci, jakie są wyniki, pisząc w jego odpowiedzi twierdzenia. - Lightness Races in Orbit