2010-03-30 7 views
21

En JavaScript, ¿por qué una cadena de número octal se convierte en un número decimal? Puedo lanzar una cadena literal hexadecimal usando Number() o +, ¿por qué no un octal?¿Por qué no un literal octal como un lanzamiento de cadena a un número?

Por ejemplo:

1000 === +"1000" // -> true 
0xFF === +"0xFF" // -> true 
0100 === +"0100" // -> false - +"0100" gives 100, not 64 

Sé que puedo analizar con parseInt("0100" [, 8]), pero me gustaría saber por qué la fundición no funciona como lo hace con los números dec hexagonal y.

Además, ¿alguien sabe por qué los literales octales se eliminan de ECMAScript 5th Edition en modo estricto?

Respuesta

22

Llego un poco tarde a la pregunta, pero creo que puedo dar una buena respuesta.

La respuesta aceptada no te dice nada más que lo que en realidad se sabe, y la mención en la propia pregunta: Number(value) obras como +value pero no como parseInt(value).

La clave es saber que existe una diferencia semántica entre conversión de tipo y parsing.

¿Por qué una cadena de número octal se emite como un número decimal?

Debido a que el Number constructor called as a Function (Number(value)) y el Unary + Operator (+value) detrás de las escenas utilice el funcionamiento interno ToNumber. El propósito de esos constructos es tipo conversión.

Cuando ToNumber is applied to the String Type se usa una producción de gramática especial, llamada StringNumericLiteral.

Esta producción puede contener solamente literales decimales y literales hexadecimal entero:

... 

StrNumericLiteral ::: 
    StrDecimalLiteral 
    HexIntegerLiteral 

...

También hay diferencias semánticas entre esta gramática y la gramática de "normal" NumericLiterals.

A StringNumericLiteral:

  • puede estar precedida y/o seguida por espacios en blanco y/o terminadores de línea.
  • Eso es decimal puede tener cualquier número de 0 dígitos iniciales. sin octals!
  • Ese decimal puede ir precedido de + o - para indicar su signo.
  • Eso está vacío o solo contiene espacio en blanco se convierte en +0.

Ahora voy a ir con las funciones parseInt y parseFloat.

El propósito de esas funciones, obviamente, es parsing, que es semánticamente diferente a conversión de tipo, por ejemplo:

parseInt("20px");  // 20 
parseInt("10100", 2); // 20 
parseFloat("3.5GB"); // 3.5 
// etc.. 

la pena mencionar que el algoritmo de parseInt cambió en el quinto ECMAScript Edición Especificación, ya no interpreta la raíz de un número como octal solo por tener un cero inicial:

parseInt("010"); // 10, ECMAScript 5 behavior 
parseInt("010"); // 8, ECMAScript 3 behavior 

Como puede ver, eso introdujo un incompatibility en el comportamiento entre las implementaciones ES3 y ES5, y como siempre se recomienda utilizar el argumento raíz, para evitar cualquier posible problema.

Ahora la segunda pregunta:

Por qué literales octales se lanzan desde un ECMAScript 5ª edición en modo estricto?

En realidad, este esfuerzo de deshacerse de literales octales viene desde 1999. Las producciones literales octales (OctalIntegerLiteral y OctalEscapeSequence) fueron retirados de la gramática de NumericLiteral s ya que el ECMAScript 3rd Edition specification, que podrían ser incluidos para backwards compatibility (also in ES5) con versiones anteriores del estándar.

De hecho, están incluidos en todas las implementaciones principales, pero técnicamente una implementación compatible con ES3 o ES5 podría optar por no incluirlos, ya que se describen como no normativa.

Ese fue el primer paso, ahora ECMAScript 5 Strict Mode los deshabilita por completo.

¿Pero por qué?

Debido a que fueron considerados como una función propensos de error, y de hecho, en el pasado causaron no intencional o difícil de atrapar insectos - al igual que el mismo problema de octales implícitas de parseInt -.

Ahora, bajo el modo estricto, un literal octal causará una excepción de SyntaxError, actualmente solo observable en Firefox 4.0 Betas.

+1

Esta es una gran respuesta y más de lo que esperaba originalmente. Supongo que pasé por alto 'StringNumericLiteral' en la especificación, y ciertamente no sabía que se permitía el espacio en blanco. Esa es solo una de esas cosas, siempre esperé que el espacio en blanco resultara en * NaN *. –

+2

Gracias @Andy, sí, a menudo veo gente sorprendida de que, por ejemplo, 'isNaN (" \ t \ r \ n ")' devuelve 'false';) – CMS

4

Debido a que en realidad no estás realizando un casting en el sentido correcto (JS no tiene casting), solo tienes que hacer malabarismos.

Cuando tiene un literal en Javascript y representa un método en él, se crea un objeto detrás de escena para usted.

"foo".toUpperCase() por ejemplo, se sustituye por la evaluación de código que más o menos el siguiente aspecto new String("foo").toUpperCase();

Dado que las cadenas no pueden ser evaluados con un + operador unitario, JS convierte la cadena en un número - y que doesn No use parseInt() o parseFloat() internamente - lo adivinó - usa Number().

Por lo tanto, el valor que ve es el que vería en el retorno de Number(), que no parece asumir octals.

+0

Gracias Peter, ya asumí 'Number()' para ser utilizado cuando unary "casting" (http://stackoverflow.com/ questions/61088/hidden-features-of-javascript/2243631 # 2243631), me parece extraño que 'Number()' no acepte ningún literal numérico codificado como lo define la gramática. Simplemente parece que hubiera tenido más sentido reutilizar el código detrás de escena que ya existe para analizar literales numéricos. Gracias por la información sobre la creación de objetos entre bastidores, lo he leído antes y se me olvidó, es fácil olvidar estas cosas cuando están mágicamente hechas para ti :-) –

+0

Siempre me molesta cuando uno de mis respuestas no se acepta porque el sistema SE no te dice cuál era, así que pensé que sería educado y te dejaría saber a dónde fueron tus 15 puntos. CMS escribió una buena respuesta que explicaba los motivos con más detalle, por lo que aceptar su respuesta parecía apropiada. Lo siento, y gracias por su respuesta :-) –

+0

@Andy no se preocupe - Estoy de acuerdo - él tiene la mejor respuesta. Aclamaciones. –

1

Para explicar por qué se eliminó el soporte octal en ES5, es principalmente porque, para principiantes o no programadores, la sintaxis es inesperada. Imagine alinear verticalmente una serie de números (tal vez agregados), usando ceros iniciales para alinearlos, por ejemplo, si sus números no usan 8 o 9, terminarán siendo tratados como octal. ¡De repente, tu código no está en las malas hierbas sin una razón obvia! Esta es la razón por la que se eliminó el soporte octal. Una sintaxis octal diferente podría agregarse algún día si no crea semejante desgracia (creo que recuerdo haber visto 0o755 como una idea simple), pero por ahora octal está fuera.

Respecto al cambio parseInt incompatible anotado en respuestas anteriores: ninguna implementación ha realizado este cambio, y sospecho que no habrá implementación. ES5 está principalmente basado en la realidad. Sus nuevas características generalmente no rompen el código existente (excepto que el nuevo código, por supuesto, debe tener cuidado al usar nuevas funciones para no romper el código existente como parte de ese uso) que no intenta usar las nuevas características. Sus incompatibilidades son en su mayoría insignificantes también, o son irrelevantes porque las implementaciones del mundo real alegremente ignoraron la especificación por razones de compatibilidad. Pero no todas las incompatibilidades están bien fundadas: algunas son más ambiciosas que la armonización. El cambio a parseInt es un ejemplo de un cambio de aspiración. Rompe el código existente que espera que la sintaxis octal, sin una raíz explícita, se pueda analizar como octal.

Para el lapso de unos pocos días SpiderMonkey (motor de JavaScript de Mozilla) implementó un medio camino de cambio para hacer parseInt, cuando se llama desde código de modo estricto, octal indiferencia, y para apoyar octal cuando no se llama desde el código de modo estricto. Esto está más cerca de lo que quiere ES5, pero es un claro impedimento para convertir el código no estricto al modo estricto, probablemente sea confuso para el usuario y, quizás lo más interesante para los implementadores, significa que no puede implementar parseInt en JavaScript mismo (porque no hay forma en la especificación para examinar la rigurosidad de la función de llamada de uno), como podría desearse en el futuro (para reducir la superficie de ataque, facilitar la implementación, etc.). Así que deshicimos la dependencia. (Escribí el parche para hacer parseInt dependiente de llamadas, y revisé el parche para deshacerlo, generado por una discusión posterior después de que mi parche inicial aterrizó.) parseInt ahora se ajusta a ES3 de nuevo, y dada la web como está, y que ES5 la semántica probablemente no sea compatible con ella, dudo que cambiemos. Por lo tanto, dudo que otros cambien tampoco. (También estoy bastante seguro de que estarían de acuerdo con nuestra estimación del grado de incompatibilidad de la web con la prohibida aspiración de ES5 de sintaxis octal implícita en parseInt, y probablemente también con nuestras otras razones. Incluso si cambiáramos, estoy no estoy seguro de que lo sigan, y sospecho que serían inteligentes para no hacerlo).

+0

+1, gracias por la información adicional.Estoy de acuerdo, la gramática para los literales octales es bastante peligrosa para los desconocedores, a diferencia del prefijo '0x' de la gramática literal del hexadecimal que hace que se destaque entre los literales decimales. –

Cuestiones relacionadas