2015-12-28 15 views
41

Stavo rivedendo il codice per una fabbrica angularjs per capire meglio come funziona. Il codice contiene una dichiarazione if che non comprendo completamente.+ !! operatore in una dichiarazione if

In un plnkr demo l'autore ha scritto questo:

if ((+!!config.template) + (+!!config.templateUrl) !== 1) { 
    throw new Error('Expected modal to have exactly one of either `template` or `templateUrl`'); 
} 

It is slightly different in the github repo:

if (!(!config.template^!config.templateUrl)) { 
    throw new Error('Expected modal to have exactly one of either `template` or `templateUrl`'); 
} 

Ovviamente dal messaggio di errore si sta controllando per vedere se uno dei due esiste. Non sono sicuro di come giunga alla conclusione. Non sono stato in grado di trovare alcuna informazione su ^ o +!

La mia domanda è: come funziona questa istruzione if? (^ o +! o +!! specificamente)

+8

^è il bit a bit javascript Operatore XOR. – Jacques

+0

'Non sono stato in grado di trovare alcuna informazione su^o +!' Non hai usato la parola chiave giusta. cercare 'javascript operator' –

+0

Primo! prende il valore - come config.template (che è probabilmente un numero a causa del + !!), lo converte in vero o falso in base al fatto che sia zero (falso) o non zero (vero) - che inverte il senso logico - poi il secondo! lo inverte di nuovo allo stesso senso logico invece di essere invertito, quindi il + considera vero/falso come il numero 1 o 0. Quindi, se non è impostato, + !! risulterà nel * numero * 0, altrimenti sarà il * numero * 1 * indipendentemente da quale sia il valore numerico effettivo di .template *. Quindi possono fare il + tra le due espressioni. – simpleuser

risposta

72

!! converts a value to a boolean (true or false).+ quindi converte che booleano in numero, sia per 1true o 0 per falsa.

> +true 
1 
> +false 
0 

Personalmente trovo più chiaro per scrivere una cosa del genere, quando si tratta di due booleani: chiarezza

if (!config.template == !config.templateUrl) { 
    throw ... 
} 

Codice e leggibilità essere dannato, a quanto pare.

+4

Si noti che se questi due sono effettivamente booleani allora si può anche scrivere' if (config.template == config.templateUrl) {} '... tuttavia uno o entrambi questi è probabilmente una stringa con l'altro essendo un' indefinito' o 'null', motivo per cui gli operatori'! 'fanno la differenza. Sarebbe anche bello semplicemente inserire le linee in più per catturare entrambi gli errori separatamente; 'if (template && templateUrl) lancia un nuovo errore (" Saw sia template che templateUrl, che vuoi usare in realtà? "); se (! template &&! templateUrl) lancia un nuovo errore ("Non ho visto né un template né un templateURL, non ho nulla da renderizzare!"); ' –

+3

Non mi piace personalmente' + !! 'ma vale la pena notare che sarebbe ridimensiona a "atteso esattamente 1 di {3 cose} da specificare" in modo più pulito. A mio parere, più leggibile sarebbe qualcosa come 'if (truthyCount (item1, item2, item3)! == 1)' –

+0

@torazaburo Sono abbastanza sicuro che sia corretto ... c'è una negazione esterna attorno allo XOR. Ho cervello scoreggia questo? –

28

Questo è un modo leggibilità orribile per scrivere il valore di una variabile booleana, e successivamente convertirlo con conversione unario che invia un risultato 0/1 numero.

considerare:

+!!true; //this converts true to false, then to true again, and true is 1 when converted 
+!!0; //converts 0 (falsy) to true, then false, and then the numeric 0 

Tecnicamente parlando !! non è il proprio operatore, è solo il (!) operatore NOT due volte.

Conversione unaria: ECMA spec doc un unario più tenta di convertire in un numero intero. Number() sarebbe anche una conversione valida.

+0

'Number' è un sostituto adatto qui, ma' parseInt' non sarebbe utile per la conversione un * booleano * a un numero, però, giusto? – apsillers

+3

Ho spesso sentito la giustificazione che "Questo codice è così complicato/intricato/importante/qualsiasi cosa che, se non sai come analizzare questi operatori come il palmo della tua mano, non dovresti comunque scherzare con questo codice ". ma onestamente tutto ciò che sento è "Facciamo in modo che questo codice incredibilmente complicato sia ancora più difficile da analizzare!" – corsiKa

+2

errato. 'parseInt (!! x)' non è lo stesso di '+ !! x'. Il primo è 'NaN'. Correttezza a parte, non sono sicuro di cosa sia * la leggibilità * orribile * razionale. '+ !! x' è efficace e non ambiguo. (Anche se l'espressione nel suo complesso può essere semplificata come sottolinea Matt.) –

6

Nel frattempo, ^ è l'operatore XOR bit a bit.

Quando si tratta di numeri più piccoli di 2, ^ funzionerà come un valore booleano OR (||) se si considera 0 = false e 1 = true.

+0

Nota a margine, è relativamente lento in javascript a causa della necessità di convertire gli operandi in numeri interi a 32 bit e quindi di tornare a float a 64 bit. –

6

! è l'operatore logico non. È un operatore unario che converte il suo operando in un booleano e quindi lo nega. !! è solo quell'operatore due volte, il secondo ! annulla la negazione, quindi il risultato finale è solo la conversione in un valore booleano.

+ è l'operatore unario più, che converte il suo operando in un numero. Nel caso di un booleano, false diventa 0 e true diventa 1.

Quindi, +!!(expression) viene valutato come 1 se l'espressione è truthy e 0 se l'espressione è falsy.

3
if ((+!!config.template) + (+!!config.templateUrl) !== 1) { 

      0   +   0    !== 1 true 
      0   +   1    !== 1 false 
      1   +   0    !== 1 false 
      1   +   1    !== 1 true 

è uguale

if (!config.template === !config.templateUrl) { 

nonostante il contenuto delle due proprietà.

33

+ !! usa la conversione implicita per trasmettere un valore come 0 o 1 a seconda del suo valore booleano

Per la maggior parte, questo è per verificare la presenza. Ad esempio, una stringa vuota è falsa (!!"" === false), quindi non è definita e una serie di altre. Questi sono i due principali se

conversioni "Falsey"

+!!""  === 0 
+!!false  === 0 
+!!0   === 0 
+!!undefined === 0 
+!!null  === 0 
+!!NaN  === 0 

conversioni "Truthy"

+!!1   === 1 
+!!true  === 1 
+!!"Foo"  === 1 
+!!3.14  === 1 
+!![]  === 1 
+!!{}  === 1 

if ((+ !! config.template) + (+ !! config.templateUrl)! == 1)

Speriamo che questo abbia più senso a questo punto. L'oggetto config ha due proprietà che stiamo esaminando. .template e .templateUrl. Il cast implicito su 0 o 1 utilizzando +!! verrà aggiunto e quindi confrontato per garantire che non sia 1 (il che significa che sia 0 o 2) - le proprietà possono essere sia accese che spente ma non diverse.

La tabella di verità qui è la seguente:

template templateUrl (+!!) + (+!!)  !==1 
"foo"  "foo"    1 + 1   true 
undefined undefined   0 + 0   true 
undefined ""     0 + 0   true 
""   undefined   0 + 0   true 
12   ""     1 + 0   false 
""   12     0 + 1   false 
undefined "foo"    0 + 1   false 
""   "foo"    0 + 1   false 
"foo"  ""     1 + 0   false 
"foo"  undefined   1 + 0   false 

Un molto metodo più semplice a tutto questo sarebbe stato utilizzare solo la conversione implicita booleano

if (!config.template === !config.templateUrl) 
+1

Il tuo metodo più semplice è meno codice, sì, ma direi che è meno leggibile per chiunque non abbia visto quel modello prima (logico xor non compare spesso). La tua versione richiede un po 'di pensiero: "la negazione di questo è uguale alla negazione di quello" rispetto a "non questo xo non questo" (la negazione nel codice di istruzioni di OP se, ovviamente, è perché stanno controllando se questa condizione fallito). Personalmente lo spezzerei in due righe, con 'var hasValidTemplate = !! config.template^config.templateUrl; if (! hasValidTemplate) '. – Kat

+0

[continuazione - il commento precedente ha anche dimenticato il '!!' prima di 'config.templateUrl'] La coercizione' !! 'a boolean è tristemente necessaria nel codice precedente è purtroppo necessaria a causa del fatto che'^'è bit per bit xor e vogliamo xor logico, per il quale dobbiamo convertire in booleani. Un'alternativa per la leggibilità potrebbe essere 'var hasTemplate = !! config.template; var hasTemplateUrl = !! config.templateUrl; if (! (hasTemplate^hasTemplateUrl) '. – Kat

Problemi correlati