¿Qué es el operador JavaScript >>> y cómo lo usas?

Estaba mirando el código de Mozilla que agregaba un método de filtro a Array y tenía una línea de código que me confundía.

var len = this.length >>> 0; 

Nunca he visto >>> usado en JavaScript antes.
¿Qué es y para qué sirve?

No solo convierte números no numéricos en números, sino que los convierte en números que pueden expressse 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 en enteros de 32 bits. Al hacer una operación en modo bit, el número se convierte en una int de 32 bits, perdiendo cualquier fracción y un lugar más alto que 32, antes de hacer el cálculo y luego volver a convertirlo en Number.

Por lo tanto, hacer una operación en modo bit sin efecto real, como un desplazamiento hacia la derecha de 0 bits >>0 , es una forma rápida de redondear un número y garantizar que esté en el rango int de 32 bits. Además, el operador triple >>> , después de realizar su operación sin firmar, convierte los resultados de su cálculo en Número como un entero sin signo en lugar del entero con signo que los demás hacen, por lo que se puede usar para convertir los negativos a 32 bits. versión de dos complementos como un número grande. Usar >>>0 asegura que tienes 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. Por lo tanto, si intenta implementar array.filter de manera que duplique exactamente lo que dice el estándar ECMAScript Fifth Edition, debería convertir el número a int sin firmar de 32 bits de esta manera.

(En realidad, hay poca necesidad práctica de esto, ya que es de esperar que las personas no establezcan array.length en 0.5 , -1 , 1e21 o 'LEMONS' 1e21 'LEMONS' . Pero estamos hablando de autores de JavaScript, por lo que nunca se sabe. ..)

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, están definidos como comportándose como flotadores. No me sorprendería si algún motor de JavaScript realmente utilizara ints cuando pudiera, por razones de rendimiento. Pero eso sería un detalle de implementación que no podría tomar ningún Ventaja de.)

El operador de desplazamiento a la derecha sin signo se utiliza en todas las implementaciones de métodos extra de la matriz de Mozilla, para garantizar que la propiedad de length sea ​​un entero sin signo de 32 bits .

La propiedad de length de los objetos de la matriz se describe en la especificación como:

Cada objeto Array tiene una propiedad de longitud cuyo valor siempre es un entero no negativo menor que 2 32 .

Este operador es la forma más rápida de lograrlo, los métodos de matriz interna utilizan la operación ToUint32 , pero ese método no es accesible y existe en la especificación para fines de implementación.

Las implementaciones de Mozilla array extras intentan ser compatibles con ECMAScript 5 , consulte la descripción del método Array.prototype.indexOf (§ 15.4.4.14):

 1. Sea O el resultado de llamar a ToObject pasando este valor 
    como el argumento
 2. Deje que lenValue sea el resultado de llamar al método interno [de [Get]] de O con 
    el argumento "longitud".
 3. Deje que len sea ToUint32 (lenValue) .
 ....

Como puede ver, solo quieren reproducir el comportamiento del método ToUint32 para cumplir con la especificación ES5 en una implementación de ES3, y como dije antes, el operador de desplazamiento a la derecha sin signo es la manera más fácil.

Ese es el operador de cambio de bit derecho sin signo . La diferencia entre esto y el operador de desplazamiento de bit derecho firmado , es que el operador de desplazamiento de bit derecho sin signo ( >>> ) se llena con ceros desde la izquierda, y el operador de desplazamiento de bit a la derecha ( >> ) se llena con el bit de signo, 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é fue utilizado:

Al cambiar cualquier dirección por 0 , se devuelve el número original y se arrojará null a 0 . Parece que el código de ejemplo que está viendo utiliza this.length >>> 0 para asegurarse de que len es numérico incluso si this.length no está definido.

Para muchas personas, las operaciones a nivel de bit no son claras (y Douglas Crockford / jslint sugiere no usar tales cosas). No significa que sea incorrecto 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 asegurar que len sea 0 es 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 ), en contraposición a >> , el operador de cambio a la derecha con signo .

>>> cambia los resultados del desplazamiento de números negativos porque no conserva el bit de signo al cambiar . Las consecuencias de esto se pueden entender por ejemplo, a partir 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, según el ejemplo "cabbage" anterior. Creo que en este caso es seguro asumir que this.length nunca será < 0 . Sin embargo, yo diría que este ejemplo es un hack desagradable , por dos razones:

  1. El comportamiento de <<< cuando se usan números negativos, un efecto secundario probablemente no sea intencionado (o probable) en el ejemplo anterior.

  2. La intención del código no es obvia , ya que la existencia de esta pregunta se verifica.

La mejor práctica es, probablemente, usar algo más legible a menos que el rendimiento sea absolutamente crítico:

 isNaN(parseInt(foo)) ? 0 : parseInt(foo) 

Dos razones:

  1. El resultado de >>> es una “integral”

  2. undefined >>> 0 = 0 (dado que JS intentará forzar el LFS al contexto numérico, esto funcionará para “foo” >>> 0, etc.)

Recuerde que los números en JS tienen una representación interna de doble. Es solo una forma “rápida” de cordura de entrada básica para la longitud.

Sin embargo , -1 >>> 0 (¡Uy, probablemente no sea la longitud deseada!)