¿Qué es el operador >>> de JavaScript y cómo se utiliza?
Estaba mirando el código de Mozilla que agrega un método de filtro a Array y tenía una línea de código que me confundió.
var len = this.length >>> 0;
Nunca antes había visto >>> usado en JavaScript.
¿Qué es y para qué sirve?
No solo convierte números que no son números en números, sino que los convierte en números que pueden expresarse como entradas sin signo de 32 bits.
Aunque los números de JavaScript son flotantes de doble precisión (*), los operadores bit a bit ( <<
, >>
, y ) se definen en términos de &
operaciones con enteros de 32 bits. Al realizar una operación bit a bit, el número se convierte en un int con signo de 32 bits, perdiendo fracciones y bits superiores a 32, antes de realizar el cálculo y luego volver a convertirlo a Número.|
~
Por lo tanto, realizar una operación bit a bit sin ningún efecto real, como un desplazamiento hacia la derecha de 0 bits >>0
, es una forma rápida de redondear un número y asegurarse de que esté en el rango int de 32 bits. Además, el >>>
operador triple, después de realizar su operación sin signo, convierte los resultados de su cálculo a Número como un entero sin signo en lugar del entero con signo que hacen los demás, por lo que puede usarse para convertir negativos al complemento a dos de 32 bits. versión como un número grande. El uso >>>0
garantiza que tenga un número entero entre 0 y 0xFFFFFFFF.
En este caso, esto es útil porque ECMAScript define los índices de matriz en términos de entradas sin signo de 32 bits. Entonces, si está tratando de implementar array.filter
de una manera que duplique exactamente lo que dice el estándar ECMAScript Quinta Edición, debería convertir el número a un int sin signo de 32 bits de esta manera.
En realidad, hay poca necesidad práctica de esto , ya que es de esperar que la gente no array.length
establezca 0.5
, o .-1
1e21
'LEMONS'
Resumen:
1>>>0 === 1
-1>>>0 === 0xFFFFFFFF -1>>0 === -1
1.7>>>0 === 1
0x100000002>>>0 === 2
1e21>>>0 === 0xDEA00000 1e21>>0 === -0x21600000
Infinity>>>0 === 0
NaN>>>0 === 0
null>>>0 === 0
'1'>>>0 === 1
'x'>>>0 === 0
Object>>>0 === 0
(*: bueno, se definen como que se comportan como flotantes. No me sorprendería si algún motor de JavaScript realmente usara ints cuando podría, por razones de rendimiento. Pero ese sería un detalle de implementación que no podría tomar Ventaja de.)
El operador de desplazamiento a la derecha sin signo se utiliza en todas las implementaciones de métodos adicionales de matriz de Mozilla, para garantizar que la length
propiedad sea un entero de 32 bits sin signo .
La length
propiedad de los objetos de matriz se describe en la especificación como:
Cada objeto Array tiene una propiedad de longitud cuyo valor es siempre un entero no negativo menor que 2 32 .
Este operador es la forma más corta de lograrlo, los métodos de matriz internamente usan la ToUint32
operación, pero ese método no es accesible y existe en la especificación para fines de implementación.
Las implementaciones de los extras de la matriz de Mozilla intentan ser compatibles con ECMAScript 5 ; consulte la descripción del Array.prototype.indexOf
método (§ 15.4.4.14):
1. Sea O el resultado de llamar a ToObject pasando este valor como el argumento. 2. Sea lenValue el resultado de llamar al método interno [[Get]] de O con el argumento "longitud". 3. Sea len ToUint32(lenValue) . ....
Como puede ver, solo quieren reproducir el comportamiento del ToUint32
método para cumplir con la especificación ES5 en una implementación ES3 y, como dije antes, el operador de desplazamiento a la derecha sin firmar es la forma más fácil.
Ese es el operador de desplazamiento de bit a la derecha sin firmar . La diferencia entre esto y el operador de desplazamiento de bits a la derecha con signo es que el operador de desplazamiento de bits a la derecha sin signo ( >>> ) se rellena con ceros desde la izquierda, y el operador de desplazamiento de bits a la derecha con signo ( >> ) se rellena con el bit de signo, por lo tanto preservando el signo del valor numérico cuando se desplaza.
Driis ha explicado suficientemente qué es el operador y qué hace. Aquí está el significado detrás de esto/por qué se usó:
Al cambiar cualquier dirección con 0
do, se devuelve el número original y se transmitirá null
a 0
. Parece que el código de ejemplo que está viendo se utiliza this.length >>> 0
para garantizar que len
sea numérico incluso si this.length
no está definido.
Para muchas personas, las operaciones bit a bit no están claras (y Douglas Crockford/jslint sugiere no usar tales cosas). No significa que esté mal hacerlo, pero existen métodos más favorables y familiares para hacer que el código sea más legible. Una forma más clara de garantizarlo len
es 0
cualquiera de los dos métodos siguientes.
// Cast this.length to a number
var len = +this.length;
o
// Cast this.length to a number, or use 0 if this.length is
// NaN/undefined (evaluates to false)
var len = +this.length || 0;
>>>
es el operador de desplazamiento a la derecha sin signo ( consulte la página 76 de la especificación JavaScript 1.5 ), a diferencia de >>
, el operador de desplazamiento a la derecha con signo .
>>>
cambia los resultados del desplazamiento de números negativos porque no conserva el bit de signo al realizar el desplazamiento . Las consecuencias de esto se pueden entender con el ejemplo de un intérprete:
$ 1 >> 0
1
$ 0 >> 0
0
$ -1 >> 0
-1
$ 1 >>> 0
1
$ 0 >>> 0
0
$ -1 >>> 0
4294967295
$(-1 >>> 0).toString(16)
"ffffffff"
$ "cabbage" >>> 0
0
Entonces, lo que probablemente se pretende hacer aquí es obtener la longitud, o 0 si la longitud no está definida o no es un número entero, como en el "cabbage"
ejemplo anterior. Creo que en este caso es seguro asumir que eso this.length
nunca sucederá < 0
. Sin embargo, yo diría que este ejemplo es un truco desagradable , por dos razones:
El comportamiento de
<<<
cuando se utilizan números negativos, es un efecto secundario que probablemente no se pretendía (o no es probable que ocurra) en el ejemplo anterior.La intención del código no es obvia , como lo comprueba la existencia de esta pregunta.
Probablemente la mejor práctica sea utilizar algo más legible a menos que el rendimiento sea absolutamente crítico:
isNaN(parseInt(foo)) ? 0 : parseInt(foo)