¿Qué hace una tilde cuando precede a una expresión?

Respuestas:

267

~es un operador bit a bit que voltea todos los bits en su operando.

Por ejemplo, si su número fuera 1, su representación binaria del flotador IEEE 754 (cómo trata JavaScript los números) sería ...

0011 1111 1111 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000

Entonces ~convierte su operando a un entero de 32 bits (los operadores bit a bit en JavaScript hacen eso) ...

0000 0000 0000 0000 0000 0000 0000 0001

Si fuera un número negativo, se almacenaría en el complemento de 2: invierta todos los bits y agregue 1.

... y luego voltea todos sus bits ...

1111 1111 1111 1111 1111 1111 1111 1110

Entonces, ¿para qué sirve? ¿Cuándo podría uno usarlo?

Tiene bastantes usos. Si estás escribiendo cosas de bajo nivel, es útil. Si ha perfilado su aplicación y ha encontrado un cuello de botella, podría ser más eficaz mediante el uso de trucos bit a bit (como una posible herramienta en una bolsa mucho más grande).

También es un truco (generalmente) poco claro para convertir indexOf()el valor de retorno encontrado en verdadero (mientras que no se encuentra como falso ) y la gente lo usa a menudo por su efecto secundario de truncar números a 32 bits (y soltar su decimal al duplicarlo) efectivamente lo mismo que Math.floor()para los números positivos).

Digo que no está claro porque no es inmediatamente obvio para qué se está utilizando. En general, desea que su código se comunique claramente con otras personas que lo leen. Si bien el uso ~puede verse bien , generalmente es demasiado inteligente para su propio bien. :)

También es menos relevante ahora que JavaScript tiene Array.prototype.includes()y String.prototype.includes(). Estos devuelven un valor booleano. Si su (s) plataforma (s) de destino lo admiten, debería preferir esto para probar la existencia de un valor en una cadena o matriz.

alex
fuente
2
¿Es desagradable la palabra correcta? Si funciona, lo llamaría un modismo del idioma. Hay muchos modismos. Una vez que los aprendes, no quedan claros. Las comprensiones de listas no están claras en Python si no las conoce y puede lograrse con bucles más detallados, pero nunca le pediría a un programador de Python que no las use. Del mismo modo, value = value || defaulten JavaScript es un idioma común y válido siempre que sepa cuándo puede y no puede usarlo.
gman
3
@gman Supongo que realmente no importa si alguien lo usa o no. Creo que comparar las comprensiones de la lista (función del lenguaje) con esto no es realmente lo mismo ( forma inteligente de evitar escribir algunos caracteres adicionales). Si crees que desagradable es un término demasiado duro, no dudes en editar mi respuesta.
alex
2
Quizás un ejemplo más común es v = t ? a : b;. Me parece mucho más claro de var v; if (t} { v = a; } else { v = b; }lo que normalmente se divide en más de 5 líneas y también más claro de var v = b; if (t) { v = a; }lo que normalmente sería 4+ líneas. Pero conozco a muchas personas que no están familiarizadas con los ? :operadores que preferirían la segunda o tercera vía. Me parece que el primero es más legible. Estoy de acuerdo con el principio general, aclare el código, no use hacks. Supongo que solo veo ~v.indexOf('...')que es muy claro una vez que lo he aprendido.
gman
9
Cuando trabajas en una gran corporación con muchos desarrolladores, quieres que el código esté claramente escrito (INGLÉS) y bien documentado. El objetivo de la codificación de alto nivel en un lenguaje con recolección de basura es evitar pensar en operaciones binarias, y muchos desarrolladores front-end ni siquiera tienen experiencia en lenguaje ensamblador.
usuario2867288
3
No llamaría ~idiomático. técnicamente es parte de la especificación del lenguaje , pero no es tanto parte del lenguaje en uso general .
worc
109

Usarlo antes de una indexOf()expresión efectivamente le da un resultado verdadero / falso en lugar del índice numérico que se devuelve directamente.

Si el valor de retorno es -1, entonces ~-1es 0porque -1es una cadena de todos los 1 bits. Cualquier valor mayor o igual a cero dará un resultado distinto de cero. Así,

if (~someString.indexOf(something)) {
}

hará que el ifcódigo se ejecute cuando "algo" esté en "someString". Si intenta usarlo .indexOf()directamente como booleano, eso no funcionará porque a veces devuelve cero (cuando "algo" está al comienzo de la cadena).

Por supuesto, esto también funciona:

if (someString.indexOf(something) >= 0) {
}

y es considerablemente menos misterioso

A veces también verás esto:

var i = ~~something;

Usar el ~operador dos veces así es una forma rápida de convertir una cadena a un entero de 32 bits. El primero ~realiza la conversión y el segundo ~voltea los bits. Por supuesto, si el operador se aplica a algo que no se puede convertir a un número, se obtiene NaNcomo resultado. ( editar - en realidad es el segundo ~que se aplica primero, pero se entiende la idea).

Puntiagudo
fuente
2
Para aquellos que no quieren negar poco a poco, ~cuando se realiza en enteros es igual a -(x + 1).
Fabrício Matté
Parece que, bueno, ya sabes, los valores numéricos NEGATIVOS también deberían devolver los valores booleanos negativos en primer lugar. Pero supongo que solo otro de los JS 'falla.
wwaawaw
77
@adlwalrus bien, la tradición del 0ser falsey el ser distinto de cero truese remontan, al menos, a C en los años 70 y probablemente a muchos otros lenguajes de programación de sistemas contemporáneos. Probablemente se deba a la forma en que funciona el hardware; muchas CPU establecen un bit cero después de una operación y tienen una instrucción de bifurcación correspondiente para probarlo.
Puntiagudo
44
Sería una forma más rápida de convertirlo a 32 bits int | 0, en cuyo caso es su única operación.
alex
@alex, de hecho, aunque no necesariamente podemos confiar en el tiempo de ejecución para no interpretar una aplicación simple ~~exactamente de la misma manera.
Puntiagudo
29

El ~es NOT en modo bit del operador , ~xes más o menos lo mismo que -(x+1). Es más fácil de entender, más o menos. Entonces:

~2;    // -(2+1) ==> -3

Considere -(x+1). -1puede realizar esa operación para producir a 0.

En otras palabras, si se ~usa con un rango de valores numéricos, se producirá un valor falso (coacción a falsepartir de 0) solo para el -1valor de entrada; de lo contrario, cualquier otro valor verdadero.

Como sabemos, -1comúnmente se llama valor centinela . Se utiliza para muchas funciones que devuelven >= 0valores para el éxito y -1para el fracaso en lenguaje C. Que la misma regla de valor de retorno de indexOf()en JavaScript.

Es común verificar la presencia / ausencia de una subcadena en otra cadena de esta manera

var a = "Hello Baby";

if (a.indexOf("Ba") >= 0) {
    // found it
}
if (a.indexOf("Ba") != -1) { 
    // found it
}

if (a.indexOf("aB") < 0) { 
    // not found
}
if (a.indexOf( "aB" ) == -1) { 
    // not found
}

Sin embargo, sería más fácil hacerlo de la ~siguiente manera

var a = "Hello Baby";

~a.indexOf("Ba");         // -7   -> truthy
if (~a.indexOf("Ba")) {   // true
    // found it
}

~a.indexOf("aB");         // 0    -> falsy
!~a.indexOf("aB");        // true
if (!~a.indexOf( "aB" )) {  // true
    // not found
}

You Don't Know JS: Tipos y gramática por Kyle Simpson

zangw
fuente
1
Es definitivamente más fácil de entender a su valor nominal, incluso si una persona no entiende el fondo que hace que funcione. Echaría un segundo vistazo -(x+1)si lo viera en una declaración if. La tilde me dice exactamente qué está haciendo para compensar la naturaleza basada en 0 de Javascript. Además, cuanto menos paréntesis, mejor para leer
Joe regular
En su bloque de código de verificación inicial, podría escribir menos utilizando if (a.indexOf("Ba") > -1) {// found} //true, lo que, aunque es un poco más largo que los ejemplos de tilde, es considerablemente menor que los dos ejemplos que dio y para los nuevos programadores var opinion = !~-1 ? 'more' : 'less'comprensibles.
HalfMens
24

~indexOf(item) surge con bastante frecuencia, y las respuestas aquí son geniales, pero tal vez algunas personas solo necesitan saber cómo usarlo y "omitir" la teoría:

   if (~list.indexOf(item)) {
     // item in list
   } else {
     // item *not* in list
   }
Jorge Bucaran
fuente
1
Estoy de acuerdo. La Guía de estilo JavaScript de Airbnb no permite ++y --porque "fomentan el engaño excesivo" y, sin embargo, de alguna manera ~sobrevivieron (acechando en las sombras) github.com/airbnb/javascript/issues/540
Shanimal
@Shanimal La alternativa es list.indexOf(item) >= 0o ... > -1ya que javascript está basado en cero y no eligió abordar esto desde el principio. Además, solo la opinión (igual que la de Airbnb), cualquiera que esté haciendo algo significativo en JavaScript lo sabe ++, y aunque --es menos común, se puede inferir el significado.
Regular Joe
@RegularJoe Estoy de acuerdo con la mayoría de eso. Personalmente no lo he necesitado ++y hace --un tiempo debido a métodos primitivos como map, forEachetc. Mi punto es más sobre por qué no consideraron ~demasiado complicado cuando cualquier estándar utilizado incluye operadores de incremento y decremento. Prohibir algo para que CIS101 no tenga ningún sentido.
Shanimal
11

Para aquellos que consideran usar el truco tilde para crear un valor verdadero a partir de un indexOfresultado, es más explícito y tiene menos magia para usar el includesmétodoString .

'hello world'.includes('hello') //=> true
'hello world'.includes('kittens') //=> false

Tenga en cuenta que este es un nuevo método estándar a partir de ES 2015, por lo que no funcionará en navegadores antiguos. En los casos en que eso sea importante, considere usar el String.prototype.includes polyfill .

Esta característica también está disponible para matrices que usan la misma sintaxis :

['apples', 'oranges', 'cherries'].includes('apples') //=> true
['apples', 'oranges', 'cherries'].includes('unicorns') //=> false

Aquí está el Array.prototype.incluye polyfill si necesita soporte de navegador más antiguo.

Dana Woodman
fuente
2
Evite el uso de incluye (). No es compatible con ninguna versión de IE (no solo los navegadores más antiguos) en el momento de la escritura: developer.mozilla.org/en/docs/Web/JavaScript/Reference/…
Onshop
8
O simplemente use un compilador, para que pueda escribir un código más claro, en lugar de escribir el idioma de acuerdo con el peor intérprete JS de denominador común ...
Kyle Baker
2
@Ben tiene razón, tampoco funciona en Netscape 4.72.
mikemaccana