¿Quién hubiera pensado que los raros y misteriosos operadores bit a bit de JavaScript podrían ser tan útiles? Descubrí este truco hace un tiempo en una presentación sobre rendimiento de JavaScript, de Thomas Fuchs. El "truco" es el siguiente:
El operador NOT a nivel de bits (~
) tomará su operando, lo convertirá a un entero de 32 bits, e invertirá cada bit de manera que cada 0
se convierta en 1
y viceversa.
00000000000000000000000000001001 ...se convierte 11111111111111111111111111110110
El efecto de esto, dado la expresión ~foo
es -(foo + 1)
. Una operación de doble negación a nivel de bits (~~foo
) por lo tanto resultará en -(-(foo + 1) + 1)
. Esto solo sigue siendo cierto para enteros; dada todas las posibles operandos, la expresión equivalente real a ~~
probablemente sea algo como:
typeof foo === 'number' && !isNaN(foo) && foo !== Infinity ? foo > 0 ? Math.floor(foo) : Math.ceil(foo) : 0; // Esto es SOLO efectivamente lo mismo... esto NO es lo que sucede internamente!
Esto es obviamente bastante más lento que ~~
.
Si el operando es un número y no es NaN
o Infinity
, entonces ~~
tendrá el efecto de redondearlo hacia cero (Math.ceil
para negativos, Math.floor
para positivos). Si no es un número, entonces creo que la función interna ToInt32
lo convierte en cero.
Aquí tienes algunos ejemplos de la operación de doble negación a nivel de bits en todo su esplendor:
~~null; // => 0 ~~undefined; // => 0 ~~0; // => 0 ~~{}; // => 0 ~~[]; // => 0 ~~(1/0); // => 0 ~~false; // => 0 ~~true; // => 1 ~~1.2543; // => 1 ~~4.9; // => 4 ~~(-2.999); // => -2
Las capacidades de ~~
hacen que sea una mejor alternativa a Math.floor
si sabes que estás trabajando con números positivos, es más rápido y ocupa menos caracteres. Sin embargo, no es tan legible, pero espero que ~~
se convierta lentamente en una técnica muy conocida en el ámbito de JS, para que todos podamos usarla sin temor a acusaciones.
Es bastante útil para normalizar argumentos que se espera que también sean enteros. Toma este ejemplo de lo que el MDC recomienda para proporcionar Array.prototype.indexOf
a navegadores que no lo admiten:
/Array.prototype.indexOf = function.../
var from = Number(arguments[1]) || 0;
from = (from < 0)
? Math.ceil(from)
: Math.floor(from);
Entra ~~
:
/Array.prototype.indexOf = function.../ var from = ~~arguments[1];