Doble NOT a nivel de bits (~~) - James Padolsey

contenido

¿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];

Resumir
Los operadores bit a bit en JavaScript, como el operador NOT (~), pueden ser útiles para redondear números hacia cero. Por ejemplo, ~~foo redondea foo hacia cero de manera más rápida que Math.floor o Math.ceil. ~~null, ~~undefined, ~~0, ~~{}, ~~[], ~~(1/0), ~~false, ~~true, ~~1.2543, ~~4.9 y ~~(-2.999) devuelven 0, 0, 0, 0, 0, 0, 0, 1, 1, 4 y -2 respectivamente. Este truco es útil para normalizar argumentos que se esperan que sean enteros. Por ejemplo, ~~arguments[1] puede reemplazar Number(arguments[1]) || 0; Math.ceil y Math.floor. Este truco puede ser más rápido y ocupar menos caracteres. Es importante tener en cuenta que ~~foo solo redondea hacia cero si foo es un número y no es NaN o Infinity. Si no es un número, ~~foo devolverá 0. Este truco puede ser útil para mejorar el rendimiento en ciertos casos, pero es importante considerar su legibilidad y comprensión para otros desarrolladores.