Quem poderia imaginar que os raros e misteriosos operadores bit a bit do JavaScript poderiam ser tão úteis? Descobri esse truque pela primeira vez há algum tempo em uma apresentação sobre desempenho do JavaScript, por Thomas Fuchs. O "truque" é o seguinte:
O operador NOT bit a bit (~
) irá pegar seu operando, convertê-lo para um inteiro de 32 bits, e inverterá cada bit de forma que cada 0
se torne um 1
e vice-versa.
00000000000000000000000000001001 ...torna-se 11111111111111111111111111110110
O efeito disso, dado a expressão ~foo
é -(foo + 1)
. Uma operação dupla de NÃO bit a bit (~~foo
) resultará portanto em -(-(foo + 1) + 1)
. Isso só permanece verdadeiro para inteiros; dado todos os possíveis operandos, a expressão equivalente real para ~~
provavelmente é algo como:
typeof foo === 'number' && !isNaN(foo) && foo !== Infinity ? foo > 0 ? Math.floor(foo) : Math.ceil(foo) : 0; // Isso é APENAS efetivamente o mesmo... isso NÃO é o que acontece internamente!
Isso é obviamente um pouco mais lento do que ~~
.
Se o operando for um número e não for NaN
ou Infinity
, então ~~
terá o efeito de arredondá-lo em direção a zero (Math.ceil
para negativo, Math.floor
para positivo). Se não for um número, então acredito que a função interna ToInt32
o converte para zero.
Aqui estão alguns exemplos da operação de NÃO bit a bit duplo em todo o seu 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
As capacidades de arredondamento de '~~' tornam-no uma alternativa melhor para 'Math.floor' se você souber que está lidando com números positivos - é mais rápido e ocupa menos caracteres. No entanto, não é tão legível, mas espero que '~~' se torne lentamente uma técnica muito conhecida na arena do JS, para que todos possamos usá-la sem temer acusações.
É bastante útil para normalizar argumentos que você espera que sejam inteiros também. Pegue este exemplo do que o MDC recomenda para fornecer Array.prototype.indexOf
para navegadores que não oferecem suporte: