Hva gjør !! (dobbel utropstegn) operatoren i JavaScript?
Jeg så denne koden:
this.vertical = vertical !== undefined ? !!vertical : this.vertical;
Det ser ut til å bruke !! som en operator, som jeg ikke kjenner igjen. Hva gjør den?
Svar #1
Den konverterer Object til boolean. Hvis det var falsk (f.eks. 0, null, undefined, osv.), ville det være false, ellers true.
!object // Inverted Boolean
!!object // Noninverted Boolean, so true Boolean representation
Så !! er ikke en operator; det er bare !-operatoren to ganger.
Det er generelt enklere å gjøre:
Boolean(object) // Boolean
Reelt verdens eksempel "Test IE-versjon":
const isIE8 = !! navigator.userAgent.match(/MSIE 8.0/);
console.log(isIE8); // Returns true or false
Hvis du ⇒
console.log(navigator.userAgent.match(/MSIE 8.0/));
// Returns either an Array or null
Men hvis du ⇒
console.log(!!navigator.userAgent.match(/MSIE 8.0/));
// Returns either true or false
Svar #2
Det er en fryktelig uklar måte å gjøre en typekonvertering på.
! betyr IKKE. Så !true er false, og !false er true. !0 er true, og !1 er false.
Så du konverterer en verdi til en Boolean, inverterer den, og inverterer den så igjen.
// Maximum Obscurity:
val.enabled = !!userId;
// Partial Obscurity:
val.enabled = (userId != 0) ? true : false;
// And finally, much easier to understand:
val.enabled = (userId != 0);
// Or just
val.enabled = Boolean(userId);
Merk: de to midterste uttrykkene er ikke helt ekvivalente med det første uttrykket når det gjelder noen spesielle tilfeller (for eksempel når userId er []) på grunn av måten !=-operatoren fungerer på og hvilke verdier som anses som sanne.
Svar #3
!!expr (to !-operatorer etterfulgt av et uttrykk) returnerer den primitive true eller false avhengig av sannhetsverdien til uttrykket.
Det gir mening når det brukes på ikke-boolean uttrykk. Noen eksempler:
Merk: Boolean-funksjonen gir nøyaktig samme resultater, og den er mer lesbar.
!!false // false
!!true // true
!!0 // false
!!parseInt("foo") // false — NaN is falsy
!!1 // true
!!-1 // true — negative number is truthy
!!(1/0) // true — Infinity 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 value is falsy
!!undefined // false — undefined primitive is falsy
!!null // false — null is falsy
!!{} // true — an (empty) object is truthy
!![] // true — an (empty) array is truthy
Svar #4
Brygg litt te:
!! er ikke en operator. Det er dobbeltbruk av ! -- som er den logiske "ikke"-operatoren.
I teorien:
! avgjør "sannheten" av hva en verdi ikke er:
Sannheten er at
falseikke ertrue(derfor gir!falseresultatettrue)Sannheten er at
trueikke erfalse(derfor gir!trueresultatetfalse)
!! bestemmer "sannheten" av hva en verdi ikke ikke er:
Sannheten er at
trueikke er ikketrue(derfor resulterer!!trueitrue)Sannheten er at
falseikke er ikkefalse(derfor resulterer!!falseifalse)
Det vi ønsker å fastslå i sammenligningen er "sannheten" om verdien til en referanse, ikke verdien av selve referansen. Det finnes et brukstilfelle der vi kanskje ønsker å vite sannheten om en verdi, selv om vi forventer at verdien er false (eller falsy), eller hvis vi forventer at verdien ikke er av typen boolean.
I praksis:
Tenk på en kortfattet funksjon som oppdager funksjonalitet (og i dette tilfellet, plattformkompatibilitet) ved hjelp av dynamisk typing (også kjent som "duck typing"). Vi ønsker å skrive en funksjon som returnerer true hvis brukerens nettleser støtter HTML5 <audio>-elementet, men vi ønsker ikke at funksjonen skal kaste en feil hvis <audio> er udefinert; og vi ønsker ikke å bruke try ... catch for å håndtere mulige feil (fordi de er ekle); og også ønsker vi ikke å bruke en sjekk inne i funksjonen som ikke konsekvent avslører sannheten om funksjonen (for eksempel, document.createElement('audio') vil fortsatt lage et element kalt <audio> selv om HTML5 <audio> ikke støttes).
Her er de tre tilnærmingene:
// 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
Hver funksjon aksepterer et argument for en <tag> og en attribute å se etter, men de returnerer hver forskjellige verdier basert på hva sammenligningene avgjør.
Men vent, det er mer!
Noen av dere la antagelig merke til at i dette spesifikke eksempelet, kunne man rett og slett sjekke for en egenskap ved å bruke den litt mer ytelsesvennlige måten å sjekke om objektet det gjelder har en egenskap. Det finnes to måter å gjøre dette på:
// 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
Vi kommer på avveie...
Uansett hvor sjeldne disse situasjonene måtte være, kan det finnes noen scenarier hvor den mest konsise, mest effektive, og dermed mest foretrukne måten å få true fra en ikke-boolean, muligens udefinert verdi faktisk er ved å bruke !!. Forhåpentligvis gjør dette det latterlig klart.
Svar #5
!! konverterer verdien til høyre for den til sin tilsvarende Boolean-verdi. (Tenk på det som en billig måte å «type-kaste» på.) Dens hensikt er vanligvis å formidle til leseren at koden ikke bryr seg om hvilken verdi som er i variabelen, men hva dens «sannhetsverdi» er.
Svar #6
!!foo bruker den unære not-operatoren to ganger og brukes til å konvertere til en Boolsk type, tilsvarende bruken av unær pluss +foo for å konvertere til et tall og å sammenkoble en tom streng ''+foo for å konvertere til en streng.
I stedet for disse trikene, kan du også bruke konstruktørfunksjonene som tilsvarer de primitive typene (uten å bruke new) for å eksplisitt kaste verdier, altså,
Boolean(foo) === !!foo
Number(foo) === +foo
String(foo) === ''+foo
Svar #7
Så mange svar gjør bare halve jobben. Ja, !!X kunne leses som «sannhetsverdien til X [representert som en Boolean]». Men !! er, praktisk talt, ikke så viktig for å finne ut om en enkelt variabel er (eller selv om mange variabler er) sann eller falsk. !!myVar === true er det samme som bare myVar. Å sammenligne !!X med en «ekte» Boolean er egentlig ikke nyttig.
Det eneste du får med !! er evnen til å sjekke sannhetsverdien til flere variabler mot hverandre på en repeterbar, standardisert (og JSLint-vennlig) måte.
Bare kaster :(
Det er...
0 === falseerfalse.!!0 === falseertrue.
Det ovenstående er ikke så nyttig. if (!0) gir deg de samme resultatene som if (!!0 === false). Jeg kan ikke komme på et godt tilfelle for å kaste en variabel til Boolean og deretter sammenligne med en «true» Boolean.
Se «== og !=» fra JSLints veiledning (merk: nettstedet har endret seg; dette er en arkivert kopi) for litt om hvorfor:
Operatorene == og != utfører typekoherens før sammenligning. Dette er dårlig fordi det gjør at '\t\n' == 0 blir sant. Dette kan skjule typefeil. JSLint kan ikke pålitelig avgjøre om == brukes riktig, så det er best å ikke bruke == og != i det hele tatt, og alltid bruke de mer pålitelige === og !== operatorene i stedet.
Hvis du bare bryr deg om at en verdi er sann eller falsk, bruk da kortformen. I stedet for
(foo != 0)bare si
(foo)og i stedet for
(foo == 0)si
(!foo)
Merk at det finnes noen ulogiske tilfeller hvor en Boolean blir omgjort til et tall (true blir omgjort til 1 og false til 0) når man sammenligner en Boolean med et tall. I dette tilfellet kan !! være mentalt nyttig. Men, igjen, dette er tilfeller hvor du sammenligner en ikke-Boolean med en hardkodet Boolean, noe som etter min mening er en alvorlig feil. if (-1) er fortsatt veien å gå her.
| Original | Lik | Resultat | Notater |
|---|---|---|---|
if (-1 == true) console.log("spam") | if (-1 == 1) | udefinert | |
if (-1 == false) console.log("spam") | if (-1 == 0) | udefinert | |
if (true == -1) console.log("spam") | if (1 == -1) | udefinert | Rekkefølge spiller ingen rolle... |
if (!!-1 == true) console.log("spam") | if (true == true) | søppelpost | bedre |
if (-1) console.log("spam") | if (sant) | søppelpost | fortsatt best |
Og ting blir enda villere avhengig av motoren din. WScript, for eksempel, vinner prisen.
function test()
{
return (1 === 1);
}
WScript.echo(test());
På grunn av noen historiske Windows-bommerter vil det gi -1 i en meldingsboks! Prøv det i et cmd.exe-vindu og se! Men WScript.echo(-1 == test()) gir deg fortsatt 0, eller WScripts false. Se bort. Det er forferdelig.
Sammenligne sannhetsverdi :)
Men hva om jeg har to verdier jeg trenger å sjekke for lik sannhet/lik falskhet?
Se for deg at vi har myVar1=0; og myVar2=undefined;.
myVar1 === myVar2er0 === undefinedog er åpenbart falsk.!!myVar1 === !!myVar2er!!0 === !!undefinedog er sant! Samme sannhetsverdi! (I dette tilfellet, begge "har en sannhetsverdi som er falsy".)
Så det eneste stedet du virkelig trenger å bruke «Boolean-cast-variabler» ville være hvis du hadde en situasjon hvor du sjekker om begge variablene har samme sannhetsverdi, ikke sant? Det vil si, bruk !! hvis du trenger å se om to variabler er begge sanne eller begge usanne (eller ikke), det vil si, av lik (eller ikke) sannhetsverdi.
Jeg kan ikke umiddelbart komme på en god, ikke-konstruert brukstilfelle for det. Kanskje du har «lenkede» felt i et skjema?
if (!!customerInput.spouseName !== !!customerInput.spouseAge ) {
errorObjects.spouse = "Please either enter a valid name AND age "
+ "for your spouse or leave all spouse fields blank.";
}
Så nå, hvis du har en sann verdi for begge eller en falsk verdi for både ektefelles navn og alder, kan du fortsette. Ellers har du bare ett felt med en verdi (eller et veldig tidlig arrangert ekteskap) og må lage en ekstra feil i din errorObjects-samling.
Selv i dette tilfellet er !! virkelig overflødig. Én ! var nok til å konvertere til en Boolean, og du sjekker bare likhet.
REDIGER 24. okt. 2017, 6. feb. 19:
Tredjepartsbiblioteker som forventer eksplisitte boolske verdier
Her er et interessant tilfelle... !! kan være nyttig når tredjepartsbiblioteker forventer eksplisitte boolske verdier.
Reager
For eksempel har False i JSX (React) en spesiell betydning som ikke utløses av enkel falskhet. Hvis du prøvde å returnere noe som følgende i JSX-en din, og forventet en int i messageCount...
{messageCount && <div>You have messages!</div>}... du kan bli overrasket over å se at React render 0 når du har null meldinger. Du må eksplisitt returnere false for at JSX ikke skal rendre. Utsagnet ovenfor returnerer 0, som JSX gladelig render, slik det skal. Det kan ikke vite at du ikke hadde Count: {messageCount}.
En løsning involverer bangbang, som tvinger
0til!!0, som erfalse:{!!messageCount && <div>Du har meldinger!</div>}JSX-dokumentasjonen foreslår at du er mer eksplisitt, skriver selvforklarende kode, og bruker en sammenligning for å tvinge til en Boolean.
{messageCount > 0 && <div>Du har meldinger!</div>}Jeg er mer komfortabel med å håndtere falskhet selv med en ternær operator --
{messageCount ? <div>Du har meldinger!</div> : false}
TypeScript
Det samme gjelder i TypeScript: Hvis du har en funksjon som returnerer en Boolean (eller du tildeler en verdi til en Boolean-variabel), kan du [vanligvis] ikke returnere/tildele en sannhetsverdi; det må være en sterkt typet boolean. Dette betyr, hvis myObject er sterkt typet, fungerer return !myObject; for en funksjon som returnerer en Boolean, men return myObject; gjør ikke det. Du må return !!myObject (eller caste til riktig Boolean på en annen måte) for å matche TypeScripts forventninger.
Unntaket for TypeScript? Hvis
myObjectvar enany, er du tilbake i JavaScripts ville vesten og kan returnere det uten!!, selv om returtypen din er en Boolean.
Husk at dette er JSX- og TypeScript-konvensjoner, ikke konvensjoner som er iboende i JavaScript.
Men hvis du ser rare 0 i din gjengitte JSX, tenk løs falsk-håndtering.
Svar #8
Det er bare den logiske IKKE-operatoren, to ganger. Den brukes for å konvertere noe til Boolean, f.eks.:
true === !!10
false === !!0
Svar #9
Den konverterer suffikset til en boolsk verdi.
Svar #10
Det ser ut til at !!-operatoren resulterer i en dobbel negasjon.
var foo = "Hello, World!";
!foo // Result: false
!!foo // Result: true
Svar #11
Det simulerer oppførselen til Boolean()-konverteringsfunksjonen. Den første NOT gir en Boolean-verdi uansett hvilken operand den får. Den andre NOT negerer den Boolean-verdien og gir dermed den true Boolean-verdien til en variabel. Sluttresultatet er det samme som å bruke Boolean()-funksjonen på en verdi.
Svar #12
Det er en dobbel not-operasjon. Den første ! konverterer verdien til Boolean og inverterer dens logiske verdi. Den andre ! inverterer den logiske verdien tilbake.
Svar #13
!! bruker NOT-operasjonen to ganger sammen. ! konverterer verdien til en Boolean og snur den, så å bruke den to ganger viser Boolean (false eller true) for den verdien. Her er et enkelt eksempel for å se hvordan !! fungerer:
Først, stedet du har:
var zero = 0;
Deretter gjør du !0. Det vil bli konvertert til Boolean og evaluert til true, fordi 0 er falsy, så du får den motsatte verdien og konvertert til Boolean, slik at det blir evaluert til true.
!zero; //true
Men vi vil ikke ha den omvendte boolske versjonen av verdien, så vi kan snu den igjen for å få resultatet vårt! Derfor bruker vi en annen !.
Basert på, !! sørger for at verdien vi får er Boolean, ikke falsk, sann, streng, osv...
Så det er som å bruke Boolean-funksjonen i JavaScript, men en enklere og kortere måte å konvertere en verdi til Boolean på:
var zero = 0;
!!zero; //false
Svar #14
! er «Boolean not», som i praksis typekaster verdien av «enable» til dets boolske motsatt. Den andre ! snur denne verdien. Så, !!enable betyr «ikke ikke enable», og gir deg verdien av enable som en Boolean.
Svar #15
Jeg synes det er verdt å nevne at en betingelse kombinert med logisk AND/OR ikke vil returnere en boolsk verdi, men den siste suksessen eller første feil i tilfelle && og den første suksessen eller siste feil i tilfelle || av betingelseskjeden.
res = (1 && 2); // res is 2
res = (true && alert) // res is function alert()
res = ('foo' || alert) // res is 'foo'
For å kaste tilstanden til en sann Boolean-literal kan vi bruke dobbel negasjon:
res = !!(1 && 2); // res is true
res = !!(true && alert) // res is true
res = !!('foo' || alert) // res is true
Svar #16
!!-konstruksjonen er en enkel måte å gjøre et hvilket som helst JavaScript-uttrykk om til dets Boolean-ekvivalent.
For eksempel: !!"he shot me down" === true og !!0 === false.
Svar #17
Det er ikke en enkeltoperatør; det er to. Det tilsvarer følgende og er en rask måte å konvertere en verdi til Boolean på.
val.enabled = !(!enable);
Svar #18
Den tvinger alle ting til Boolean.
For eksempel:
console.log(undefined); // -> undefined
console.log(!undefined); // -> true
console.log(!!undefined); // -> false
console.log('abc'); // -> abc
console.log(!'abc'); // -> false
console.log(!!'abc'); // -> true
console.log(0 === false); // -> false
console.log(!0 === false); // -> false
console.log(!!0 === false); // -> true
Svar #19
Jeg mistenker at dette er et rester fra C++ der folk overstyrer !-operatoren, men ikke bool-operatoren.
Så for å få et negativt (eller positivt) svar i det tilfellet, måtte du først bruke !-operatoren for å få en Boolean, men hvis du ønsket å sjekke den positive saken, ville du bruke !!.
Svar #20
Setningene if og while og ?-operatoren bruker sannhetsverdier for å bestemme hvilken kodegren som skal kjøres. For eksempel er null- og NaN-tall og den tomme strengen falske, men andre tall og strenger er sanne. Objekter er sanne, men verdien undefined og null er begge falske.
Dobbel negasjonsoperatoren !! beregner sannhetsverdien til en verdi. Det er egentlig to operatorer, der !!x betyr !(!x), og oppfører seg som følger:
- Hvis
xer en falsk verdi, er!xsann, og!!xerfalsk. - Hvis
xer en sann verdi, er!xfalsk, og!!xersann.
Når den brukes på toppnivået i en boolsk kontekst (if, while, eller ?), er !!-operatoren atferdsmessig en no-op. For eksempel betyr if (x) og if (!!x) det samme.
Praktiske bruksområder
Men det har flere praktiske bruksområder.
En bruk er å komprimere et objekt tapende til dets sannhetsverdi, slik at koden din ikke holder en referanse til et stort objekt og holder det i live. Å tilordne !!some_big_object til en variabel i stedet for some_big_object slipper det for søppeloppsamleren. Dette er nyttig for tilfeller som produserer enten et objekt eller en falsk verdi som null eller den udefinerte verdien, slik som nettleserfunksjonsdeteksjon.
En annen bruk, som jeg nevnte i et svar om C sitt tilsvarende !!-operator, er med "lint"-verktøy som ser etter vanlige skrivefeil og skriver ut diagnostikk. For eksempel, i både C og JavaScript, produserer noen vanlige skrivefeil for boolske operasjoner andre oppførsel hvis output ikke er helt boolsk:
if (a=b)er tildeling etterfulgt av bruk av sannhetsverdien tilb;if (a == b)er en likhets-sammenligning.if (a & b)er en bitvis OG;if (a && b)er en logisk OG.2 & 5er0(en falsk verdi);2 && 5er sann.
Operatoren !! forsikrer lint-verktøyet om at det du skrev er det du mente: utfør denne operasjonen, og ta deretter sannhetsverdien av resultatet.
En tredje bruk er å produsere logisk XOR og logisk XNOR. I både C og JavaScript utfører a && b en logisk AND (sann hvis begge sider er sanne), og a & b utfører en bitvis AND. a || b utfører en logisk OR (sann hvis minst én er sann), og a | b utfører en bitvis OR. Det finnes en bitvis XOR (eksklusiv OR) som a ^b, men det finnes ingen innebygd operator for logisk XOR (sann hvis akkurat én side er sann). Du kan for eksempel ønske å la brukeren skrive inn tekst i akkurat ett av to felt. Det du kan gjøre er å konvertere hver til en sannhetsverdi og sammenligne dem: !!x !== !!y.
Svar #21
Dette spørsmålet har blitt besvart ganske grundig, men jeg vil gjerne legge til et svar som jeg håper er så forenklet som mulig, og gjør betydningen av !! så lett å forstå som mulig.
Fordi JavaScript har det som kalles «truthy» og «falsy» verdier, finnes det uttrykk som når de evalueres i andre uttrykk vil resultere i en sann eller falsk tilstand, selv om verdien eller uttrykket som undersøkes ikke faktisk er true eller false.
For eksempel:
if (document.getElementById('myElement')) {
// Code block
}
Hvis det elementet faktisk eksisterer, vil uttrykket evalueres som sant, og kodeblokken vil bli utført.
Men :
if (document.getElementById('myElement') == true) {
// Code block
}
...vil ikke resultere i en sann tilstand, og kodeblokken vil ikke bli utført, selv om elementet eksisterer.
Hvorfor? Fordi document.getElementById() er et «truthy»-uttrykk som vil evaluere til sant i denne if()-setningen, men det er ikke en faktisk boolsk verdi av true.
Den doble "ikke" i dette tilfellet er ganske enkel. Det er rett og slett to ikke på rad.
Den første "inverterer" ganske enkelt den sannhetsverdige eller falske verdien, noe som resulterer i en faktisk Boolean-type, og deretter "inverterer" den andre den tilbake til sin opprinnelige tilstand, men nå i en faktisk Boolean-verdi. På den måten får du konsistens:
if (!!document.getElementById('myElement')) {}
og
if (!!document.getElementById('myElement') == true) {}
vil begge returnere true, som forventet.
Svar #22
Jeg ville bare legge til at
if(variableThing){
// do something
}
er det samme som
if(!!variableThing){
// do something
}
Men dette kan være et problem når noe er udefinert.
// a === undefined, b is an empty object (eg. b.asdf === undefined)
var a, b = {};
// Both of these give error a.foo is not defined etc.
// you'd see the same behavior for !!a.foo and !!b.foo.bar
a.foo
b.foo.bar
// This works -- these return undefined
a && a.foo
b.foo && b.foo.bar
b && b.foo && b.foo.bar
Trikset her er at kjeden av && vil returnere den første falsy-verdien den finner -- og dette kan mates til en if-setning osv. Så hvis b.foo er undefined, vil den returnere undefined og hoppe over b.foo.bar-setningen, og vi får ingen feil.
Ovenstående returnerer undefined, men hvis du har en tom streng, false, null, 0, undefined, vil disse verdiene returneres så snart vi møter dem i kjeden -- [] og {} er begge "truthy" og vi vil fortsette ned den såkalte "&& kjeden" til neste verdi til høyre.
P.S. En annen måte å gjøre det ovenfor på (b && b.foo) er (b || {}).foo. Disse er ekvivalente, fordi hvis b er undefined, vil b || {} være {}, og du vil få tilgang til en verdi i et tomt objekt (ingen feil) i stedet for å prøve å få tilgang til en verdi i "undefined" (forårsaker en feil).
Så, (b || {}).foo er det samme som b && b.foo og ((b || {}).foo || {}).bar er det samme som b && b.foo && b.foo.bar.
Svar #23
Det er dobbelt Boolean-negasjon. Det brukes ofte for å sjekke om en verdi ikke er udefinert.
Svar #24
Det er massevis av gode svar her, men hvis du har lest helt ned hit, hjalp dette meg å 'forstå det'. Åpne konsollen i Chrome (osv.), og begynn å skrive:
!(!(1))
!(!(0))
!(!('truthy'))
!(!(null))
!(!(''))
!(!(undefined))
!(!(new Object())
!(!({}))
woo = 'hoo'
!(!(woo))
...etc., etc., until the light goes on ;)
Naturligvis er dette alt det samme som bare å skrive !!someThing, men de ekstra parentesene kan hjelpe til med å gjøre det mer forståelig.
Svar #25
!!x er en kortform for Boolean(x).
Det første utropstegnet tvinger JavaScript-motoren til å kjøre Boolean(x), men det har også bivirkningen at det inverterer verdien. Så det andre utropstegnet opphever bivirkningen.
