Ce face operatorul !! (două semne de exclamare) în JavaScript?
Am văzut acest cod:
this.vertical = vertical !== undefined ? !!vertical : this.vertical;
Se pare că folosește !! ca operator, pe care nu îl recunosc. Ce face acesta?
Răspuns #1
Converteste Object în boolean. Dacă era falsy (de exemplu, 0, null, undefined, etc.), ar fi fost false, altfel, true.
!object // Inverted Boolean
!!object // Noninverted Boolean, so true Boolean representation
Deci !! nu este un operator; este doar operatorul ! aplicat de două ori.
Este în general mai simplu să faci:
Boolean(object) // Boolean
Exemplu din viața reală „Testați versiunea IE”:
const isIE8 = !! navigator.userAgent.match(/MSIE 8.0/);
console.log(isIE8); // Returns true or false
Dacă tu ⇒
console.log(navigator.userAgent.match(/MSIE 8.0/));
// Returns either an Array or null
Dar dacă tu ⇒
console.log(!!navigator.userAgent.match(/MSIE 8.0/));
// Returns either true or false
Răspunsul #2
Este o modalitate teribil de obscură de a face o conversie de tip.
! înseamnă NU. Deci !true este false, iar !false este true. !0 este true, iar !1 este false.
Deci convertești o valoare într-un Boolean, o inversezi, și apoi o inversezi din nou.
// 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);
Notă: cele două expresii din mijloc nu sunt exact echivalente cu prima expresie când vine vorba de unele cazuri limită (când userId este [], de exemplu) din cauza modului în care funcționează operatorul != și ce valori sunt considerate adevărate.
Răspunsul #3
!!expr (doi operatori ! urmați de o expresie) returnează valoarea primitivă true sau false în funcție de adevărul expresiei.
Are sens atunci când este utilizat pe expresii non-booleene. Câteva exemple:
Notă: funcția Boolean produce exact aceleași rezultate și este mai ușor de citit.
!!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
Răspunsul #4
Prepară niște ceai:
!! nu este un operator. Este dubla utilizare a ! -- care este operatorul logic „not”.
În teorie:
! determină „adevărul” a ceea ce o valoare **nu** este:
Adevărul este că
falsenu estetrue(de aceea!falserezultă întrue)Adevărul este că
truenu estefalse(de aceea!truerezultă înfalse)
!! determină „adevărul” a ceea ce o valoare nu nu este:
Adevărul este că
truenu este nutrue(de aceea!!truerezultă întrue)Adevărul este că
falsenu este nufalse(de aceea!!falserezultă înfalse)
Ceea ce dorim să determinăm în comparație este „adevărul” despre valoarea unei referințe, nu valoarea propriu-zisă a referinței. Există un caz de utilizare în care am putea dori să cunoaștem adevărul despre o valoare, chiar dacă ne așteptăm ca valoarea să fie false (sau falsy), sau dacă ne așteptăm ca valoarea să nu fie de tipul boolean.
În practică:
Luați în considerare o funcție concisă care detectează funcționalitatea caracteristicilor (și, în acest caz, compatibilitatea platformei) prin tipizare dinamică (cunoscută și ca „tipizare de rață”). Dorim să scriem o funcție care returnează true dacă browser-ul unui utilizator suportă elementul HTML5 <audio>, dar nu dorim ca funcția să genereze o eroare dacă <audio> este nedefinit; și nu dorim să folosim try ... catch pentru a gestiona eventualele erori (pentru că sunt neplăcute); și de asemenea nu dorim să folosim o verificare în interiorul funcției care să nu dezvăluie constant adevărul despre caracteristică (de exemplu, document.createElement('audio') va crea în continuare un element numit <audio> chiar dacă HTML5 <audio> nu este suportat).
Iată cele trei abordări:
// 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
Fiecare funcție acceptă un argument pentru un <tag> și un atribut pe care să îl caute, dar fiecare returnează valori diferite în funcție de ceea ce determină comparațiile.
Dar așteaptă, mai este și altele!
Unii dintre voi probabil ați observat că, în acest exemplu specific, cineva ar putea pur și simplu să verifice o proprietate folosind mijloacele puțin mai performante de a verifica dacă obiectul în cauză are o proprietate. Există două moduri de a face acest lucru:
// 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
Ne abatem de la subiect...
Oricât de rare ar fi aceste situații, pot exista câteva scenarii în care modul cel mai concis, cel mai performant și, prin urmare, cel mai preferat de a obține true dintr-o valoare non-booleană, posibil nedefinită, este într-adevăr folosind !!. Sper că acest lucru clarifică ridicol de mult situația.
Răspunsul #5
!! convertește valoarea de la dreapta sa în echivalentul său valoric Boolean. (Gândește-te la o modalitate simplă de „tipare” a valorilor.) Intenția sa este de obicei de a transmite cititorului că codul nu contează ce valoare este în variabilă, ci care este valoarea sa „adevărată".
Răspunsul #6
!!foo aplică operatorul unar not de două ori și este utilizat pentru a converti la tipul Boolean, similar cu utilizarea unarului plus +foo pentru a converti la număr și concatenarea unui șir gol ''+foo pentru a converti la șir.
În loc de aceste trucuri, poți folosi și funcțiile constructor corespunzătoare tipurilor primitive (fără a folosi new) pentru a converti explicit valorile, adică,
Boolean(foo) === !!foo
Number(foo) === +foo
String(foo) === ''+foo
Răspunsul #7
Atâtea răspunsuri care fac doar jumătate din muncă. Da, !!X ar putea fi citit ca „adevărul lui X [reprezentat ca un Boolean]”. Dar !! nu este, practic vorbind, atât de important pentru a afla dacă o singură variabilă este (sau chiar dacă mai multe variabile sunt) adevărată sau falsă. !!myVar === true este la fel ca simplu myVar. Compararea lui !!X cu un Boolean „real” nu este chiar utilă.
Singurul lucru pe care îl câștigi cu !! este abilitatea de a verifica veridicitatea mai multor variabile unele față de altele într-un mod repetabil, standardizat (și prietenos cu JSLint).
Pur și simplu aruncând :(
Asta este...
0 === falseestefalse.!!0 === falseestetrue.
Cele de mai sus nu sunt atât de utile. if (!0) îți oferă aceleași rezultate ca if (!!0 === false). Nu pot să mă gândesc la un caz bun pentru a converti o variabilă în Boolean și apoi a o compara cu un Boolean „true”.
Vezi „== și !=” din direcțiile JSLint (notă: site-ul s-a schimbat; aceasta este o copie arhivată) pentru puțin despre motiv:
Operatorii == și != fac conversie de tip înainte de comparare. Aceasta este rău deoarece face ca '' \t\n' == 0' să fie adevărat. Acest lucru poate ascunde erori de tip. JSLint nu poate determina în mod fiabil dacă == este folosit corect, așa că este mai bine să nu folosești deloc == și != și să folosești întotdeauna operatorii mai fiabili === și !== în schimb.
Dacă te interesează doar dacă o valoare este adevărată sau falsă, atunci folosește forma scurtă. În loc de
(foo != 0)spune doar
(foo)și în loc de
(foo == 0)spune
(!foo)
Rețineți că există unele cazuri neintuitive în care un Boolean va fi convertit într-un număr (true este convertit în 1 și false în 0) atunci când se compară un Boolean cu un număr. În acest caz, !! ar putea fi util mental. Totuși, din nou, acestea sunt cazuri în care comparați un non-Boolean cu un Boolean strict tipizat, ceea ce este, după părerea mea, o greșeală gravă. if (-1) rămâne totuși modul corect de a proceda aici.
| Original | Echivalent | Rezultat | Note |
|---|---|---|---|
if (-1 == true) console.log("spam") | dacă (-1 == 1) | nedefinit | |
if (-1 == false) console.log("spam") | dacă (-1 == 0) | nedefinit | |
dacă (true == -1) console.log("spam") | dacă (1 == -1) | nedefinit | Ordinea nu contează... |
dacă (!!-1 == adevărat) console.log("spam") | dacă (adevărat == adevărat) | spam | mai bine |
dacă (-1) console.log("spam") | dacă (adevărat) | spam | încă cel mai bun |
Și lucrurile devin și mai nebunești în funcție de motorul tău. WScript, de exemplu, câștigă premiul.
function test()
{
return (1 === 1);
}
WScript.echo(test());
Din cauza unor bizare istorice ale Windows, asta va afișa -1 într-o fereastră de mesaj! Încearcă în promptul cmd.exe și vezi! Dar WScript.echo(-1 == test()) încă îți dă 0, sau false de la WScript. Nu te uita. E oribil.
Comparând veridicitatea :)
Dar dacă am două valori pe care trebuie să le verific dacă sunt ambele adevărate sau false?
Să presupunem că avem myVar1=0; și myVar2=undefined;.
myVar1 === myVar2este0 === undefinedși este evident fals.!!myVar1 === !!myVar2este!!0 === !!undefinedși este adevărat! Aceeași valoare de adevăr! (În acest caz, ambele „au o valoare de adevăr falsă”.)
Deci singurul loc unde ai avea cu adevărat nevoie să folosești „variabile transformate în Boolean” ar fi dacă ai avea o situație în care verifici dacă ambele variabile au aceeași veridicitate, nu? Adică, folosește !! dacă trebuie să vezi dacă două variabile sunt ambele adevărate sau ambele false (sau nu), adică de veridicitate egală (sau nu).
Nu pot să mă gândesc la un caz de utilizare grozav, neforțat pentru asta din moment. Poate ai câmpuri „legate” într-un formular?
if (!!customerInput.spouseName !== !!customerInput.spouseAge ) {
errorObjects.spouse = "Please either enter a valid name AND age "
+ "for your spouse or leave all spouse fields blank.";
}
Așadar, acum dacă ai un adevărat pentru ambele sau un fals pentru ambele, numele și vârsta soțului, poți continua. Altfel, ai doar un câmp cu o valoare (sau o căsătorie aranjată foarte devreme) și trebuie să creezi o eroare suplimentară în colecția ta errorObjects.
Chiar și în acest caz, !! este cu adevărat superfluu. Un ! era suficient pentru a face conversia la Boolean, și tu verifici doar egalitatea.
EDIT 24 oct 2017, 6 feb 19:
Biblioteci terțe care așteaptă valori booleene explicite
Iată un caz interesant... !! ar putea fi util atunci când bibliotecile terțe se așteaptă la valori booleene explicite.
Reacționează
De exemplu, False în JSX (React) are un înțeles special care nu este declanșat de simpla falsitate. Dacă ai încercat să returnezi ceva asemănător cu următorul în JSX-ul tău, așteptând un int în messageCount...
{messageCount && <div>You have messages!</div>}... ai putea fi surprins să vezi React afișând un 0 atunci când nu ai mesaje. Trebuie să returnezi explicit false pentru ca JSX să nu renderizeze. Afirmația de mai sus returnează 0, pe care JSX îl afișează fără probleme, așa cum ar trebui. Nu poate să știe că nu ai avut Count: {messageCount}.
O soluție implică bangbang, care forțează
0să devină!!0, care estefalse:{!!messageCount && <div>Ai mesaje!</div>}Documentația JSX sugerează să fii mai explicit, să scrii cod auto-explicativ și să folosești o comparație pentru a forța obținerea unei valori Boolean.
{messageCount > 0 && <div>Ai mesaje!</div>}Mă simt mai confortabil să gestionez falsitatea singur cu un ternar --
{messageCount ? <div>Ai mesaje!</div> : false}
TypeScript
Același lucru în TypeScript: Dacă ai o funcție care returnează un Boolean (sau atribui o valoare unei variabile Boolean), de obicei nu poți returna/atribuia o valoare de tip boolean-y; trebuie să fie un Boolean strict tipizat. Aceasta înseamnă că, dacă myObject este strict tipizat, return !myObject; funcționează pentru o funcție care returnează un Boolean, dar return myObject; nu. Trebuie să return !!myObject (sau să faci cast la Boolean-ul corespunzător în alt mod) pentru a corespunde așteptărilor TypeScript.
Excepția pentru TypeScript? Dacă
myObjectera unany, te-ai întors în Vestul Sălbatic al JavaScript și poți să-l returnezi fără!!, chiar dacă tipul de returnare este Boolean.
Țineți cont că acestea sunt convenții JSX și TypeScript, nu unele inerente JavaScript.
Dar dacă vezi 0-uri ciudate în JSX-ul redat, gândește-te la o gestionare laxă a valorilor false.
Răspunsul #8
Este doar operatorul logic NOT, de două ori. Este folosit pentru a converti ceva în Boolean, de exemplu:
true === !!10
false === !!0
Răspuns #9
Transformă sufixul într-o valoare Booleană.
Răspuns #10
Se pare că operatorul !! duce la o dublă negație.
var foo = "Hello, World!";
!foo // Result: false
!!foo // Result: true
Răspuns #11
Simulează comportamentul funcției de conversie Boolean(). Prima NOT returnează o valoare Booleană indiferent de operandul pe care îl primește. A doua NOT neagă acea valoare Boolean și astfel oferă valoarea true a unei variabile. Rezultatul final este același ca și folosirea funcției Boolean() asupra unei valori.
Răspunsul #12
Este o operațiune dublă not. Primul ! convertește valoarea în Boolean și inversează valoarea sa logică. Al doilea ! inversează valoarea logică înapoi.
Răspunsul #13
!! folosește operațiunea NOT de două ori împreună. ! convertește valoarea într-un Boolean și o inversează, astfel că folosind-o de două ori, arată Boolean-ul (false sau true) al acelei valori. Iată un exemplu simplu pentru a vedea cum funcționează !!:
La început, locul pe care îl ai:
var zero = 0;
Apoi faci !0. Va fi convertit în Boolean și evaluat ca true, pentru că 0 este falsy, așa că obții valoarea inversă și convertită în Boolean, astfel încât va fi evaluată ca true.
!zero; //true
Dar nu vrem versiunea Boolean inversată a valorii, așa că o putem inversa din nou pentru a obține rezultatul nostru! De aceea folosim un alt !.
Practic, !! ne asigură că valoarea pe care o obținem este Boolean, nu falsy, truthy, string etc...
Deci este ca și cum ai folosi funcția Boolean în JavaScript, dar într-un mod mai simplu și mai scurt de a converti o valoare în Boolean:
var zero = 0;
!!zero; //false
Răspuns #14
! este „negarea booleană”, care esențial convertește valoarea lui „enable” în opusul său boolean. Al doilea ! inversează această valoare. Deci, !!enable înseamnă „nu nu enable”, oferindu-ți valoarea lui enable ca Boolean.
Răspuns #15
Cred că merită menționat că o condiție combinată cu AND/OR logic nu va returna o valoare Booleană, ci ultimul succes sau primul eșec în cazul && și primul succes sau ultimul eșec în cazul || al lanțului de condiții.
res = (1 && 2); // res is 2
res = (true && alert) // res is function alert()
res = ('foo' || alert) // res is 'foo'
Pentru a transforma condiția într-un literal Boolean adevărat putem folosi dubla negație:
res = !!(1 && 2); // res is true
res = !!(true && alert) // res is true
res = !!('foo' || alert) // res is true
Răspunsul #16
Construcția !! este o modalitate simplă de a transforma orice expresie JavaScript în echivalentul său Boolean.
De exemplu: !!"he shot me down" === true și !!0 === false.
Răspunsul #17
Nu este un singur operator; sunt doi. Este echivalent cu următoarele și este o metodă rapidă de a converti o valoare în Boolean.
val.enabled = !(!enable);
Răspunsul #18
Forțează toate lucrurile să fie booleene.
De exemplu:
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
Răspunsul #19
Bănui că acesta este un rest din C++ unde oamenii suprascriu operatorul !, dar nu și operatorul bool.
Deci, pentru a obține un răspuns negativ (sau pozitiv) în acel caz, ar trebui mai întâi să folosești operatorul ! pentru a obține un Boolean, dar dacă ai vrea să verifici cazul pozitiv, ai folosi !!.
Răspunsul #20
Instrucțiunile if și while și operatorul ? folosesc valori de adevăr pentru a determina care ramură de cod să fie executată. De exemplu, numerele zero și NaN și șirul gol sunt false, dar alte numere și șiruri sunt true. Obiectele sunt true, dar valoarea undefined și null sunt ambele false.
Operatorul de dublă negație !! calculează valoarea de adevăr a unei valori. De fapt, sunt doi operatori, unde !!x înseamnă !(!x) și se comportă astfel:
- Dacă
xeste o valoare falsă,!xestetrue, iar!!xestefalse. - Dacă
xeste o valoare adevărată,!xestefalse, iar!!xestetrue.
Când este folosit la nivelul superior al unui context Boolean (if, while sau ?), operatorul !! se comportă, practic, ca un no-op. De exemplu, if (x) și if (!!x) înseamnă același lucru.
Utilizări practice
Totuși, are mai multe utilizări practice.
Unul dintre modurile de utilizare este comprimarea cu pierdere a unui obiect la valoarea sa de adevăr, astfel încât codul tău să nu păstreze o referință la un obiect mare și să-l mențină activ. Alocarea lui !!some_big_object unei variabile în loc de some_big_object permite eliberarea lui pentru colectorul de gunoi. Acest lucru este util în cazurile care produc fie un obiect, fie o valoare falsă, cum ar fi null sau valoarea nedeterminată, cum ar fi detectarea caracteristicilor browserului.
O altă utilizare, pe care am menționat-o într-un răspuns despre operatorul corespunzător !! din C, este cu uneltele „lint” care caută greșeli de tastare frecvente și afișează diagnostice. De exemplu, atât în C cât și în JavaScript, câteva greșeli de tastare comune pentru operațiile booleene produc alte comportamente ale căror rezultate nu sunt tocmai booleene:
if (a=b)este atribuirea urmată de utilizarea valorii de adevăr ab;if (a == b)este o comparație de egalitate.if (a & b)este un AND pe biți;if (a && b)este un AND logic.2 & 5este0(o valoare falsă);2 && 5este adevărat.
Operatorul !! liniștește instrumentul de analiză statică că ceea ce ai scris este ceea ce ai intenționat: efectuează această operație, apoi preia valoarea de adevăr a rezultatului.
O a treia utilizare este pentru a produce XOR logic și XNOR logic. Atât în C, cât și în JavaScript, a && b realizează un AND logic (adevărat dacă ambele părți sunt adevărate), iar a & b realizează un AND pe biți. a || b realizează un OR logic (adevărat dacă cel puțin unul este adevărat), iar a | b realizează un OR pe biți. Există un XOR pe biți (exclusive OR) ca a ^b, dar nu există un operator încorporat pentru XOR logic (adevărat dacă exact o parte este adevărată). De exemplu, ați putea dori să permiteți utilizatorului să introducă text în exact unul dintre cele două câmpuri. Ce puteți face este să convertiți fiecare într-o valoare de adevăr și să le comparați: !!x !== !!y.
Răspuns #21
Această întrebare a fost răspunsă destul de detaliat, dar aș dori să adaug un răspuns pe care sper să-l fac cât mai simplu posibil, făcând semnificația lui !! cât se poate de ușor de înțeles.
Pentru că JavaScript are ceea ce se numesc valori „adevărate” și „false”, există expresii care, atunci când sunt evaluate în alte expresii, vor rezulta într-o condiție adevărată sau falsă, chiar dacă valoarea sau expresia examinate nu sunt de fapt true sau false.
De exemplu:
if (document.getElementById('myElement')) {
// Code block
}
Dacă acel element există într-adevăr, expresia va fi evaluată ca adevărată și blocul de cod va fi executat.
Totuși :
if (document.getElementById('myElement') == true) {
// Code block
}
...nu va rezulta într-o condiție adevărată, iar blocul de cod nu va fi executat, chiar dacă elementul există.
De ce? Pentru că document.getElementById() este o expresie „adevărată” care va fi evaluată ca adevărată în această declarație if(), dar nu este o valoare Booleană reală de true.
Dublul „not” în acest caz este destul de simplu. Este pur și simplu două not puse unul după altul.
Primul pur și simplu „inversează” valoarea adevărată sau falsă, rezultând un tip Boolean propriu-zis, iar al doilea o „inversează” înapoi la starea sa inițială, dar acum într-o valoare Booleană reală. În acest fel ai consistență:
if (!!document.getElementById('myElement')) {}
și
if (!!document.getElementById('myElement') == true) {}
vor returna ambele true, așa cum era de așteptat.
Răspunsul #22
Am vrut doar să adaug că
if(variableThing){
// do something
}
este la fel ca
if(!!variableThing){
// do something
}
Dar aceasta poate fi o problemă atunci când ceva este nedefinit.
// 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
Trucul aici este că lanțul de &&-uri va returna prima valoare falsă pe care o găsește -- și aceasta poate fi folosită într-o declarație if etc. Deci, dacă b.foo este nedefinit, va returna nedefinit și va sări peste declarația b.foo.bar, iar noi nu obținem nicio eroare.
Mai sus returnează undefined, dar dacă ai un șir gol, false, null, 0, undefined, acele valori vor fi returnate de îndată ce le întâlnim în lanț -- [] și {} sunt ambele „adevărate” și vom continua în jos pe așa-numitul „lanț &&” către următoarea valoare din dreapta.
P.S. O altă modalitate de a face ceea ce este descris mai sus (b && b.foo) este (b || {}).foo. Acestea sunt echivalente, deoarece dacă b este nedefinit, atunci b || {} va fi {}, și vei accesa o valoare într-un obiect gol (fără eroare) în loc să încerci să accesezi o valoare în cadrul „undefined” (ce cauzează o eroare).
Deci, (b || {}).foo este același lucru cu b && b.foo și ((b || {}).foo || {}).bar este același lucru cu b && b.foo && b.foo.bar.
Răspunsul #23
Este o dublă negație booleană. Este adesea folosită pentru a verifica dacă o valoare nu este nedefinită.
Răspunsul #24
Există o mulțime de răspunsuri grozave aici, dar dacă ai citit până aici, asta m-a ajutat să „înțeleg”. Deschide consola în Chrome (etc.) și începe să tastezi:
!(!(1))
!(!(0))
!(!('truthy'))
!(!(null))
!(!(''))
!(!(undefined))
!(!(new Object())
!(!({}))
woo = 'hoo'
!(!(woo))
...etc., etc., until the light goes on ;)
În mod natural, toate acestea sunt la fel ca și cum ai tasta pur și simplu !!someThing, dar parantezele adăugate ar putea ajuta să fie mai ușor de înțeles.
Răspunsul #25
!!x este prescurtarea pentru Boolean(x).
Primul semn de exclamare forțează motorul JavaScript să ruleze Boolean(x), dar are și efectul secundar de a inversa valoarea. Așadar, al doilea semn de exclamare anulează efectul secundar.
