281 votos

¿Cómo puedo evitar JavaScript parseInt octal comportamiento?

Trate de ejecutar el siguiente JavaScript:

parseInt('01'); //equals 1
parseInt('02'); //equals 2
parseInt('03'); //equals 3
parseInt('04'); //equals 4
parseInt('05'); //equals 5
parseInt('06'); //equals 6
parseInt('07'); //equals 7
parseInt('08'); //equals 0 !!
parseInt('09'); //equals 0 !!

He aprendido de la manera difícil que JavaScript piensa que el cero indica un entero octal, y dado que no existe un "8" o "9" en la base 8, la función devuelve cero. Guste o no, esto es así por diseño.

¿Cuáles son las soluciones?

Nota: en aras de la exhaustividad, estoy a punto de publicar una solución, pero es una solución que odio, así que por favor publicar otros/respuestas mejor.


Actualización: la 5ª Edición de JavaScript estándar (ECMA-262) introduce un cambio de hora que elimina este comportamiento. Mozilla tiene una buena escritura.

326voto

Paolo Bergantino Puntos 199336

Este es un común Javascript gotcha con una solución simple:

Sólo especificar la base, o 'base', tal que así:

parseInt('08',10); // 8

Usted también podría usar Número:

Number('08'); // 8

63voto

xfix Puntos 2890

Primero de todo, usted realmente no necesita parseInt() en la mayoría de los casos. Es el algoritmo está lleno de peculiaridades, la 0 prefijo es incluso prohibida por la especificación ("la especificación de la función parseInt ya no permite implementaciones para el tratamiento de Cadenas de caracteres que empiezan con un carácter 0 como valores octales."), pero que tomará un tiempo para cambiar de navegador comportamientos (aunque estoy seguro de que nadie hace uso octals intencionalmente en parseInt()). Y Internet Explorer 6 nunca va a cambiar (el Internet Explorer 9 sin embargo, se eliminó el soporte para octals en parseInt()). El algoritmo utilizado por lo general no más de lo que quieres de ella. En ciertos casos, es mala idea.

  1. El primer argumento es convertido a cadena si no lo está ya.
  2. Recortar el número, así que ' 4' se convierte '4'.
  3. Compruebe si la cadena comienza con - o + y eliminar a este personaje. Si fue - hacen de salida negativo.
  4. Convertir base a entero.
  5. Si la base es 0 o NaN trate de adivinar radix. Significa mirar (no distingue mayúsculas de minúsculas) 0x y (no estándar) 0. Si el prefijo no se ha encontrado, 10 se utiliza (y esto es lo que lo más probable es que).
  6. Si la base es 16 tira 0x desde el comienzo, si es que existe.
  7. Encontrar el primer carácter que no está en el rango de la base.
  8. Si no hay nada antes del primer carácter que no estaba en el rango de radix, devolver NaN.
  9. Convertir el número decimal hasta el primer carácter que no está en el rango.

    Por ejemplo, parseInt('012z', 27) da 0 * Math.pow(27, 2) + 1 * Math.pow(27, 1) + 2 * Math.pow(27, 0).

El algoritmo en sí no es realmente rápido, pero el rendimiento varía (optimizaciones hacer maravillas). Yo he puesto a prueba en JSPerf y los resultados son interesantes. + y ~~ son de más rápido con excepción de Chrome donde parseFloat() de alguna manera es mucho más rápido que otras opciones (de 2 a 5 veces más rápido que otras opciones, donde + es en realidad 5 veces más lento). En Firefox, ~~ prueba es muy rápida en algunos casos, tengo Infinity de los ciclos.

La otra cosa es la corrección. parseInt(), ~~ y parseFloat() cometer errores en silencio. En caso de parseInt() y parseFloat() se ignoran los caracteres después carácter no válido - se puede llamar a una función (en la mayoría de los casos es anti-característica para mí, como switch declaraciones fallthrough) y si lo necesita, utilice uno de esos. En caso de ~~ significa que regresan 0, así que ten cuidado.

En ciertos casos, parseInt() podría hacerte daño. Muy mal. Por ejemplo, si el número es tan grande que está escrito en notación exponencial. Uso Math métodos de entonces.

parseInt(2e30); // will return 2

De todos modos, al final quiero hacer una lista de métodos para convertir cadenas a números (ambos enteros y flotantes). Tienen diversos usos y puede ser interesado lo que el método a utilizar. En la mayoría de los casos, la más simple es +number método, use si usted puede. Hagas lo que hagas (excepto para el primer método), todos deben dar resultado correcto.

parseInt('08', 10); // 8
+'08';              // 8
~~'08';             // 8
parseFloat('08');   // 8
Number('08');       // 8
new Number('08');   // 8... I meant Object container for 8
Math.ceil('08');    // 8

parseInt(number)

No use. Simple como eso. Utilice parseInt(number, 10) o esta solución, que por arte de magia arreglar parseInt función. Por favor, tenga en cuenta que esta solución no funcionará en JSLint. Por favor, no te quejes.

(function () {
    "use strict";
    var oldParseInt = parseInt;
    // Don't use function parseInt() {}. It will make local variable.
    parseInt = function (number, radix) {
        return oldParseInt(number, radix || 10);
    };
}());

parseInt(number, radix)

parseInt convierte el argumento de los números, utilizando el mencionado algoritmo anterior. Evitar su uso en grandes números enteros, ya que puede hacer de los resultados correctos en casos como parseInt(2e30). También, nunca dar como argumento para Array.prototype.map o Underscore.js variación de la misma, ya que puede obtener resultados raros (intentar ['1', '2', '3'].map(parseInt) si se desea (para la explicación, reemplace parseInt con console.log)).

Utilizar cuando:

  1. Cuando usted necesita para leer los datos escritos en diferentes radix.
  2. Usted necesita para ignorar los errores (por ejemplo, cambio 123px a 123)

De lo contrario usar otros más seguro de los métodos (si necesita entero, utilice Math.floor cambio).

+number

+ prefijo (+number) convierte el número a flotar. En caso de error se devuelve NaN que se puede comparar por cualquiera isNaN() o number !== number (debe devolver true sólo para NaN). Es muy rápido en Opera.

Utilice a menos que usted desee características específicas de otros tipos.

~~number

~~ es un hack que usa ~ dos veces en el entero. Como ~ operación bit a bit sólo puede ser hecho por los enteros, el número se convierte automáticamente. La mayoría de los navegadores tienen optimizaciones para este caso. Como las operaciones bit a bit sólo por debajo de Math.pow(2, 32) nunca uso este método con números grandes. Es extraordinariamente rápido en mono araña del motor.

Utilizar cuando:

  1. Estás escribiendo código, donde el rendimiento es importante para el mono araña (como los plugins de FireFox) y no necesita de detección de errores.
  2. Usted necesita entero y cuidado de la resultante de JavaScript tamaño.

parseFloat(number)

parseFloat() trabaja como + con la única excepción - número de procesos hasta que el primer carácter no válido en lugar de devolver NaN. Es muy rápido (pero no tan rápido como ~~ sobre Firefox) en V8. A diferencia parseInt variación, ésta debe ser segura con Array.prototype.map.

Utilizar cuando:

  1. Estás rendimiento de escritura de código crítico para Node.js o estás escribiendo en Google Chrome plugins (V8).
  2. Usted necesita para ignorar los errores (por ejemplo, cambio 42.13px a 42.13)

Number(number)

Evitarlo. Funciona como + prefijo y es generalmente más lento. El único uso en el que podría ser útil es de devolución de llamada para Array.prototype.map - no puede utilizar + como de devolución de llamada.

new Number(number)

Se usa cuando se necesita para confundir a todo el mundo con 0 siendo truthy valor y teniendo typeof de 'number'. En serio, no.

Métodos de matemáticas, como Math.ceil(number)

El uso de ellas cuando las necesite entero, ya que es más seguro que parseInt() por no ignorar los caracteres inesperados. Por favor, tenga en cuenta que técnicamente implica la conversión largo de cadena → float → integer → float (números en JavaScript son flotadores) - pero la mayoría de los navegador tiene optimizaciones para ella, de manera que en general no es que notable. Es también seguro con Array.prototype.map.

43voto

Karl Guertin Puntos 2206

Si usted sabe que su valor va a ser en el entero de 32 bits con signo del rango, ~~x hará lo correcto en todos los escenarios.

~~"08" === 8
~~"foobar" === 0
~~(1.99) === 1
~~(-1.99)  === -1

Si usted mira para arriba binario no (~), la especificación requiere de un "ToInt32" la conversión para el argumento que hace la evidente conversión a un Int32 y se especifica para coaccionar NaN valores a cero.

Sí, esto es increíblemente hackish pero es tan conveniente...

24voto

Portman Puntos 15878

A partir de la parseInt documentación, utilizar la opcion de radix argumento para especificar en base 10:

parseInt('08', 10); //equals 8
parseInt('09', 10); //equals 9

Esto me parece pedante, confuso y detallado (en realidad, un argumento extra en cada uno de los parseInt?) así que tengo la esperanza de que hay una Manera Mejor.

10voto

RichieHindle Puntos 98544

Especificar la base:

var number = parseInt(s, 10);

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X