In che modo !! ~ (not tilde / bang bang tilde) altera il risultato di una chiamata al metodo Array 'contains / included'?


95

Se leggi i commenti nella inArraypagina jQuery qui , c'è una dichiarazione interessante:

!!~jQuery.inArray(elm, arr) 

Ora, credo che un doppio punto esclamativo convertirà il risultato in tipo boolean, con il valore di true. Quello che non capisco è qual è l'uso dell'operatore tilde ( ~) in tutto questo?

var arr = ["one", "two", "three"];
if (jQuery.inArray("one", arr) > -1) { alert("Found"); }

Refactoring della ifdichiarazione:

if (!!~jQuery.inArray("one", arr)) { alert("Found"); }

Abbattersi:

jQuery.inArray("one", arr)     // 0
~jQuery.inArray("one", arr)    // -1 (why?)
!~jQuery.inArray("one", arr)   // false
!!~jQuery.inArray("one", arr)  // true

Ho anche notato che se metto la tilde davanti, il risultato è -2.

~!!~jQuery.inArray("one", arr) // -2

Non capisco lo scopo della tilde qui. Qualcuno può spiegarlo o indicarmi una risorsa?


50
Chiunque voglia scrivere un codice del genere deve allontanarsi dalla tastiera.
Kirk Woll

12
@KirkWoll: perché? ~jQuery.inArray()è in realtà molto utile - forse anche un'ottima ragione per cui le funzioni di ricerca restituiscono -1un errore (l'unico valore il cui complemento a due è falso). Una volta che hai visto e capito il trucco, sento che è ancora più leggibile di != -1.
Amadan

9
@Amadan - no. Proprio no. Seriamente, non posso credere che tu stia difendendo !!~per qualcosa .
Kirk Woll,

24
Il problema è che è proprio questo: un "trucco". La differenza principale tra if (x != -1)e if (~x)per me è che il primo esprime effettivamente ciò che intendi fare. Quest'ultimo esprime il tuo desiderio di fare qualcos'altro completamente ("converti il ​​mio numero a 64 bit in un intero a 32 bit e controlla se bit per bit NON di quell'intero è vero"), dove ti capita di ottenere il risultato desiderato in questo un caso.
Jimmi,

10
>= 0probabilmente non era abbastanza leet , quindi è !!~stato usato il più criptico .
Yoshi

Risposte:


56

L'operatore tilde non fa effettivamente parte di jQuery - è un operatore NOT bit per bit in JavaScript stesso.

Vedi Il grande mistero della tilde (~) .

Stai ottenendo numeri strani nei tuoi esperimenti perché stai eseguendo un'operazione logica bit per bit su un numero intero (che, per quanto ne so, può essere memorizzato come complemento a due o qualcosa del genere ...)

Il complemento di due spiega come rappresentare un numero in binario. Penso di aver avuto ragione.


3
Fisso! (L'ho modificato in un altro collegamento che, stranamente, è stato scritto dopo la mia risposta originale ...)
pglhall

121

C'è una ragione specifica che a volte vedrai ~applicato di fronte $.inArray.

Fondamentalmente,

~$.inArray("foo", bar)

è un modo più breve per farlo

$.inArray("foo", bar) !== -1

$.inArrayrestituisce l'indice dell'elemento nell'array se viene trovato il primo argomento e restituisce -1 se non viene trovato. Ciò significa che se stai cercando un valore booleano di "questo valore è nell'array?", Non puoi fare un confronto booleano, poiché -1 è un valore vero e quando $ .inArray restituisce 0 (un valore falso ), significa che si trova effettivamente nel primo elemento dell'array.

L'applicazione ~dell'operatore bit per bit fa -1diventare 0, e fa sì che 0 diventi `-1. Pertanto, non trovare il valore nell'array e applicare il NOT bit per bit risulta in un valore falso (0) e tutti gli altri valori restituiranno numeri diversi da 0 e rappresenteranno un risultato veritiero.

if (~$.inArray("foo", ["foo",2,3])) {
    // Will run
}

E funzionerà come previsto.


2
Quanto è ben supportato nei browser (ora nel 2014?) O è stato supportato perfettamente da sempre?
Pillole

Sarei sorpreso se operazioni di base come queste non fossero perfette.
pcarvalho

104

!!~exprvaluta falsequando exprè -1altrimenti true.
È uguale a expr != -1, solo rotto *


Funziona perché le operazioni bit per bit di JavaScript convertono gli operandi in interi con segno a 32 bit nel formato in complemento a due. Pertanto !!~-1viene valutato come segue:

   -1 = 1111 1111 1111 1111 1111 1111 1111 1111b // two's complement representation of -1
  ~-1 = 0000 0000 0000 0000 0000 0000 0000 0000b // ~ is bitwise not (invert all bits)
   !0 = true                                     // ! is logical not (true for falsy)
!true = false                                    // duh

Un valore diverso da -1avrà almeno un bit impostato a zero; invertendolo si creerà un valore veritiero; applicare l' !operatore due volte a un valore true restituisce booleano true.

Quando viene utilizzato con .indexOf()e vogliamo solo verificare se il risultato è -1o meno:

!!~"abc".indexOf("d") // indexOf() returns -1, the expression evaluates to false
!!~"abc".indexOf("a") // indexOf() returns  0, the expression evaluates to true
!!~"abc".indexOf("b") // indexOf() returns  1, the expression evaluates to true

* !!~8589934591restituisce falso quindi questoabominionon può essere utilizzato in modo affidabile per testare -1.


1
In una libreria stabile, non vedo alcun problema con l'utilizzo ~foo.indexOf(bar), non è un risparmio significativo sui personaggi o sulle prestazioni, ma è una scorciatoia relativamente comune allo stesso modo foo = foo || {}.
zzzzBov

6
Non è un problema ... almeno fino a quando non viene chiesto a qualcun altro di continuare con il tuo codice.
Salman A


1
@ahsteele, sono ben consapevole di questa regola, tuttavia gli operatori bit per bit fanno parte di ogni linguaggio di programmazione a cui riesco a pensare. Cerco di programmare in un modo che sia leggibile da qualcuno che possa leggere il codice . Non smetto di usare le caratteristiche di un linguaggio semplicemente perché qualcun altro non lo capisce, altrimenti non sarei nemmeno in grado di usarlo!! .
zzzzBov

A rigor di termini, >= 0non ha lo stesso comportamento di !!~. !== -1è più vicino.
Peter Olson

33

~foo.indexOf(bar)è una scorciatoia comune da rappresentare foo.contains(bar)perché la containsfunzione non esiste.

In genere il cast in booleano non è necessario a causa del concetto di JavaScript di valori "falsi". In questo caso viene utilizzato per forzare l'output della funzione a essere trueo false.


6
+1 Questa risposta spiega il "perché" meglio della risposta accettata.
nalply

18

jQuery.inArray()restituisce -1"non trovato", il cui complemento ( ~) è 0. Pertanto, ~jQuery.inArray()restituisce un valore falso ( 0) per "non trovato" e un valore vero (un numero intero negativo) per "trovato". !!formalizzerà quindi il falso / vero in booleano reale false/ true. Quindi, !!~jQuery.inArray()darà trueper "trovato" e falseper "non trovato".


13

Il ~per tutti 4 byte intè uguale a questa formula-(N+1)

COSÌ

~0   = -(0+1)   // -1
~35  = -(35+1)  // -36 
~-35 = -(-35+1) //34 

3
Questo non è sempre vero, poiché (ad esempio) ~2147483648 != -(2147483648 + 1).
Frxstrem

10

L' ~operatore è l'operatore di complemento bit per bit. Il risultato intero da inArray()è -1, quando l'elemento non viene trovato, o un numero intero non negativo. Il complemento bit per bit di -1 (rappresentato in binario come tutti i bit 1) è zero. Il complemento bit per bit di qualsiasi numero intero non negativo è sempre diverso da zero.

Pertanto, !!~isarà truequando l'intero "i" è un numero intero non negativo e falsequando "i" è esattamente -1.

Nota che ~costringe sempre il suo operando a intero; cioè, forza i valori in virgola mobile non interi a interi, così come i valori non numerici.


10

La tilde NON è bit per bit - inverte ogni bit del valore. Come regola generale, se usi ~un numero, il suo segno sarà invertito, quindi 1 verrà sottratto.

Quindi, quando lo fai ~0, ottieni -1 (0 invertito è -0, sottrarre 1 è -1).

È essenzialmente un modo elaborato e super micro ottimizzato per ottenere un valore che è sempre booleano.


8

Hai ragione: questo codice tornerà falsequando la indexOfchiamata restituirà -1; altrimenti true.

Come dici tu, sarebbe molto più sensato usare qualcosa di simile

return this.modifiedPaths.indexOf(path) !== -1;

1
Ma sono altri 3 byte da inviare al client! modifica: (scherzando a proposito, ho pubblicato il mio commento e ho capito che non era ovvio (il che è sia triste che sciocco))
Wesley Murch

@Wesley: È vero, ma deve essere inviato a ciascun client una sola volta , supponendo che il client memorizzerà nella cache il file .js. Detto questo, potrebbero usare >=0invece di !==-1- nessun byte extra da inviare e ancora più leggibile della versione bit-twiddling.
LukeH

2
Chi sta trollando chi qui? ;) Immagino di aver dato per scontato che scrivere codice leggibile sia meglio del criptico codice pre-ottimizzato che genera questo tipo di domande. Basta minimizzare più tardi e scrivere ora codice leggibile e comprensibile.
Wesley Murch

2
Personalmente direi che > -1è ancora più leggibile, ma probabilmente è molto soggettivo.
Yoshi

6

L' ~operatore è l'operatore NOT bit per bit. Ciò significa che prende un numero in forma binaria e trasforma tutti gli zeri in uno e gli uno in zero.

Ad esempio, il numero 0 in binario è 0000000, mentre -1 è 11111111. Allo stesso modo, 1 è 00000001in binario, mentre -2 è 11111110.


3

La mia ipotesi è che sia lì perché è qualche carattere più corto (che gli autori della libreria sono sempre dopo). Utilizza anche operazioni che richiedono solo pochi cicli macchina quando vengono compilate nel codice nativo (al contrario del confronto con un numero).

Sono d'accordo con un'altra risposta che è eccessivo ma forse potrebbe avere senso in un ciclo stretto (richiede una stima del guadagno di prestazioni, tuttavia, altrimenti potrebbe rivelarsi un'ottimizzazione prematura).


2

Presumo, poiché si tratta di un'operazione bit per bit, è il modo più veloce (computazionalmente economico) per verificare se il percorso appare in modifiedPaths.


1

Come (~(-1)) === 0, quindi:

!!(~(-1)) === Boolean(~(-1)) === Boolean(0) === false

1
Questo può essere accurato, ma è una spiegazione utile per l'interrogante? Affatto. Se non l'avessi capito all'inizio, una risposta concisa come questa non sarebbe stata d'aiuto.
Spudley

Penso che questa risposta abbia senso. Se hai un cervello matematico, puoi vedere chiaramente quali parti cambiano ad ogni passaggio. È la migliore risposta a questa domanda? No. Ma è utile, credo di sì! +1
Taylor Lopez
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.