¿Cómo !! ~ (no tilde / bang bang tilde) altera el resultado de una llamada al método Array 'contiene / incluido'?

95

Si lee los comentarios en la inArraypágina de jQuery aquí , hay una declaración interesante:

!!~jQuery.inArray(elm, arr) 

Ahora, creo que un signo de exclamación doble convertirá el resultado a tipo boolean, con el valor de true. Lo que no entiendo es cuál es el uso del ~operador tilde ( ) en todo esto.

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

Refactorizando la ifdeclaración:

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

Descompostura:

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

También noté que si pongo la tilde al frente, el resultado es -2.

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

No entiendo el propósito de la tilde aquí. ¿Alguien puede explicarlo o señalarme un recurso?

usuario717236
fuente
50
Quien quiera escribir un código como ese debe alejarse del teclado.
Kirk Woll
12
@KirkWoll: ¿Por qué? ~jQuery.inArray()es realmente muy útil, posiblemente incluso una muy buena razón por la cual las funciones de búsqueda regresan -1por falla (el único valor cuyo complemento a dos es falso). Una vez que haya visto y entendido el truco, creo que es aún más legible que != -1.
Amadan
9
@Amadan - no. Simplemente no. En serio, no puedo creer que te defiendas !!~por nada .
Kirk Woll
24
El problema es que es solo eso: un "truco". La principal diferencia entre if (x != -1)y if (~x)para mí es que el primero en realidad expresa lo que pretendes hacer. Este último expresa que desea hacer algo completamente diferente ("convierta mi número de 64 bits en un entero de 32 bits, y verifique si el bit a bit NO de ese entero es verdadero"), donde simplemente obtiene el resultado deseado en este Un caso.
JimmiTh
10
>= 0Probablemente no era Leet suficiente, así que cuanto más críptica !!~se utilizó.
Yoshi

Respuestas:

56

El operador de tilde no es en realidad parte de jQuery en absoluto, es un operador NOT bit a bit en JavaScript.

Ver El gran misterio de la Tilde (~) .

Obtiene números extraños en sus experimentos porque está realizando una operación lógica bit a bit en un entero (que, por lo que sé, puede almacenarse como complemento a dos o algo así ...)

El complemento a dos explica cómo representar un número en binario. Creo que tenía razón.

pglhall
fuente
3
¡Fijo! (Lo cambié a otro enlace que, curiosamente, fue escrito después de mi respuesta original ...)
pglhall
121

Hay una razón específica por la que a veces verá ~aplicado delante $.inArray.

Básicamente,

~$.inArray("foo", bar)

es una forma más corta de hacer

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

$.inArraydevuelve el índice del elemento en la matriz si se encuentra el primer argumento, y devuelve -1 si no se encuentra. Esto significa que si está buscando un booleano de "¿está este valor en la matriz?", No puede hacer una comparación booleana, ya que -1 es un valor verdadero y cuando $ .inArray devuelve 0 (un valor falso ), significa que realmente se encuentra en el primer elemento de la matriz.

La aplicación del ~operador bit a bit hace -1que se convierta 0y que 0 se convierta en `-1. Por lo tanto, no encontrar el valor en la matriz y aplicar el NOT bit a bit da como resultado un valor falso (0), y todos los demás valores devolverán números distintos de 0 y representarán un resultado veraz.

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

Y funcionará según lo previsto.

Yahel
fuente
2
¿Qué tan bien compatible es esto en los navegadores (ahora en 2014?) ¿O fue perfectamente compatible todo el tiempo?
Píldoras
Me sorprendería que operaciones básicas como estas no fueran perfectas.
pcarvalho
104

!!~exprevalúa falsecuando expres lo -1contrario true.
Es lo mismo que expr != -1, solo roto *


Funciona porque las operaciones bit a bit de JavaScript convierten los operandos en enteros con signo de 32 bits en formato de complemento a dos. Así !!~-1se evalúa de la siguiente manera:

   -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 valor distinto de -1tendrá al menos un bit puesto a cero; invertirlo creará un valor de verdad; la aplicación del !operador dos veces a un valor verdadero devuelve booleano verdadero.

Cuando se usa con .indexOf()y solo queremos verificar si el resultado es -1o no:

!!~"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

* se !!~8589934591evalúa como falso, por lo que estoabominaciónno se puede utilizar de forma fiable para realizar la prueba -1.

Salman A
fuente
1
En una biblioteca estable, no veo ningún problema con el uso ~foo.indexOf(bar), no es un ahorro significativo en personajes o rendimiento, pero es una abreviatura relativamente común de la misma manera que lo foo = foo || {}es.
zzzzBov
6
No es un problema ... no al menos hasta que se le pida a otra persona que continúe con su código.
Salman A
1
@ahsteele, soy muy consciente de esa regla, sin embargo, los operadores bit a bit son parte de todos los lenguajes de programación que se me ocurren. Intento programar de una manera que sea legible para alguien que pueda leer código . No dejo de usar las características de un idioma simplemente porque alguien más no lo entiende, de lo contrario , ni siquiera podría usarlo!! .
zzzzBov
Estrictamente hablando, >= 0no tiene el mismo comportamiento que !!~. !== -1está más cerca.
Peter Olson
33

~foo.indexOf(bar)es una abreviatura común de representar foo.contains(bar)porque la containsfunción no existe.

Normalmente, la conversión a booleana no es necesaria debido al concepto de JavaScript de valores "falsos". En este caso, se usa para forzar que la salida de la función sea trueo false.

zzzzBov
fuente
6
+1 Esta respuesta explica el "por qué" mejor que la respuesta aceptada.
finalmente,
18

jQuery.inArray()devuelve -1"no encontrado", cuyo complemento ( ~) es 0. Por lo tanto, ~jQuery.inArray()devuelve un valor falso ( 0) para "no encontrado" y un valor verdadero (un entero negativo) para "encontrado". !!luego formalizará el falso / verdadero en un booleano false/ real true. Entonces, !!~jQuery.inArray()dará truepor "encontrado" y falsepor "no encontrado".

Amadan
fuente
13

El ~para los 4 bytes intes igual a esta fórmula-(N+1)

ENTONCES

~0   = -(0+1)   // -1
~35  = -(35+1)  // -36 
~-35 = -(-35+1) //34 
Mina Gabriel
fuente
3
Esto no siempre es cierto, ya que (por ejemplo) ~2147483648 != -(2147483648 + 1).
Viernes
10

El ~operador es el operador de complemento bit a bit. El resultado entero de inArray()es -1, cuando no se encuentra el elemento, o algún entero no negativo. El complemento bit a bit de -1 (representado en binario como todos los bits 1) es cero. El complemento bit a bit de cualquier entero no negativo es siempre distinto de cero.

Así, !!~iserá truecuando el entero "i" sea un entero no negativo y falsecuando "i" sea exactamente -1.

Tenga en cuenta que ~siempre convierte su operando en entero; es decir, fuerza los valores de coma flotante no enteros a enteros, así como los valores no numéricos.

Puntiagudo
fuente
10

Tilde NO es bit a bit: invierte cada bit del valor. Como regla general, si usa ~un número, su signo se invertirá y luego se restará 1.

Por lo tanto, cuando lo hace ~0, obtiene -1 (0 invertido es -0, restar 1 es -1).

Es esencialmente una forma elaborada y súper micro optimizada de obtener un valor que siempre es booleano.

Joe
fuente
8

Tienes razón: este código volverá falsecuando la indexOfllamada devuelva -1; de lo contrario true.

Como dices, sería mucho más sensato usar algo como

return this.modifiedPaths.indexOf(path) !== -1;
LukeH
fuente
1
¡Pero son 3 bytes más para enviar al cliente! editar: (solo bromeando por cierto, publiqué mi comentario y me di cuenta de que no era obvio (lo cual es triste y tonto))
Wesley Murch
@Wesley: Eso es cierto, pero solo tiene que enviarse a cada cliente una vez , asumiendo que el cliente almacenará en caché el .js. Habiendo dicho eso, podrían usar en >=0lugar de !==-1- no hay bytes adicionales para enviar y aún más legible que la versión de bit-twidddling.
LukeH
2
¿Quién está trolleando a quién aquí? ;) Supongo que di por sentado que escribir código legible es mejor que código críptico pre-optimizado que genera este tipo de preguntas. Simplemente minimice más tarde y escriba código legible y comprensible ahora.
Wesley Murch
2
Personalmente, diría que > -1es aún más legible, pero probablemente sea muy subjetivo.
Yoshi
6

El ~operador es el operador NOT bit a bit. Lo que esto significa es que toma un número en forma binaria y convierte todos los ceros en unos y los unos en ceros.

Por ejemplo, el número 0 en binario es 0000000, mientras que -1 es 11111111. Asimismo, 1 está 00000001en binario, mientras que -2 lo es 11111110.

Frxstrem
fuente
3

Supongo que está ahí porque tiene unos pocos caracteres más corto (lo que los autores de la biblioteca siempre buscan). También utiliza operaciones que solo toman unos pocos ciclos de máquina cuando se compilan en el código nativo (a diferencia de la comparación con un número).

Estoy de acuerdo con otra respuesta de que es una exageración, pero quizás podría tener sentido en un circuito cerrado (sin embargo, requiere una estimación de ganancia de rendimiento; de lo contrario, puede resultar una optimización prematura).

Alexander Pavlov
fuente
2

Supongo que, dado que es una operación bit a bit, es la forma más rápida (computacionalmente barata) de verificar si la ruta aparece en las rutas modificadas.

panos2point0
fuente
1

Como (~(-1)) === 0, entonces:

!!(~(-1)) === Boolean(~(-1)) === Boolean(0) === false
Ingeniero
fuente
1
Esto puede ser exacto, pero ¿es una explicación útil para quien pregunta? De ningún modo. Si no lo entendiera para empezar, una respuesta concisa como esta no ayudaría.
Spudley
Creo que esta respuesta tiene sentido. Si tienes un cerebro matemático, puedes ver claramente qué partes cambian en cada paso. ¿Es la mejor respuesta a esta pregunta? No. ¡Pero es útil, creo que sí! +1
Taylor López