¿Por qué 2+ 40 es igual a 42?

360

Estaba desconcertado cuando un colega me mostró esta línea de alertas de JavaScript 42.

alert(2+ 40);

Resulta rápidamente que lo que parece un signo menos es en realidad un personaje arcano de Unicode con una semántica claramente diferente.

Esto me dejó preguntándome por qué ese personaje no produce un error de sintaxis cuando se analiza la expresión. También me gustaría saber si hay más personajes que se comportan así.

GOTO 0
fuente
28
@Elyasin ¿Copió / pegó o volvió a escribir?
user253751
44
Esto también funciona en Visual C #. Al pegar el personaje extraño en el IDE de Visual Studio, o al completar la declaración escribiendo ;, el editor tiende a cambiar el extraño `` personaje en un espacio normal, pero si deshace esa 'autocorrección', tiene el mismo comportamiento . Ese personaje tiene la misma semántica que un espacio, incluso si parece un guión o menos (en las fuentes habituales).
Jeppe Stig Nielsen
44
Lo contrario también puede suceder. Algunos idiomas que admiten identificadores Unicode aceptan caracteres Unicode que parecen espacios en blanco (en otras palabras, no puede verlos); incluso puede ser posible tener identificadores completamente invisibles.
gnasher729
58
(OT) ¿Porque 42 es una respuesta a todo?
ivan_pozdeev
44
@Thomas el hecho de que el resultado inesperado fue causado por ese personaje Unicode ya estaba claro.
GOTO 0

Respuestas:

470

Ese personaje es "OGHAM SPACE MARK" , que es un personaje espacial. Entonces el código es equivalente a alert(2+ 40).

También me gustaría saber si hay más personajes que se comportan así.

Cualquier carácter Unicode en la clase Zs es un espacio en blanco en JavaScript , pero no parece haber tantos .

Sin embargo, JavaScript también permite caracteres Unicode en los identificadores , lo que le permite usar nombres de variables interesantes como ಠ_ಠ.

Felix Kling
fuente
3
Cuadro con un código hexadecimal subrayado cuadro con un código hexadecimal. ¿Qué personaje está destinado a ser?
user253751
12
@immibis La última parte de esta respuesta es un emoticón disponible en forma de imagen en disapprovallook.com
Mark S.
3
Tenga en cuenta que no solo los Zscaracteres se consideran espacios en blanco en JavaScript. Hay más: github.com/mathiasbynens/regexpu/blob/…
Mathias Bynens
20
Mi reacción cuando ಠ_ಠse puede utilizar como un identificador en JS: ಠ_ಠ
Chris Cirefice
2
@ChrisCirefice subraya que ser tratado como una carta tiene una larga historia en lenguaje C-style. ser tratado como una carta es solo sentido común, ya que es una carta. Sería un error claro si ಠ_ಠno se pudiera utilizar como identificador.
Jon Hanna
81

Después de leer las otras respuestas, escribí un script simple para encontrar todos los caracteres Unicode en el rango U + 0000 – U + FFFF que se comportan como espacios en blanco. Como parece, hay 26 o 27 de ellos dependiendo del navegador, con desacuerdos sobre U + 0085 y U + FFFE.

Tenga en cuenta que la mayoría de estos caracteres se parecen a un espacio en blanco normal.

GOTO 0
fuente
17
U + 0085 "NEL" se define como espacio en blanco por Unicode, pero tiene una larga historia de mal manejo. U + FFFE no es un personaje sin nombre ni propiedades además de NChar y no debe considerarse espacio en blanco por nada razonable. Dicho esto, mi navegador no está de acuerdo conmigo en ambos puntos :)
hobbs
44
@hobbs U + FFFE también es un \p{Default Ignorable Code Point}, no solo un \p{Noncharacter Code Pount}. U + 0085 siempre ha sido un \p{Whitespace}punto de código. El maligno es el SEPARADOR DE VOCALES MONGOLIANO U + 180E, que "recientemente" perdió su \p{Whitespace}propiedad. Tenga en cuenta que \p{Pattern Whitespace}es un conjunto mucho más pequeño y una propiedad inmutable. Pero \p{Whitespace}no lo es.
tchrist
2
FEFFes la lista de materiales y se puede tratar como un "espacio sin interrupción de ancho cero" dentro de los textos. FFFEes su equivalente endian intercambiado. Quizás esa es la razón por la que algunos navegadores tratan es como un espacio en blanco.
CodesInChaos
ecma-international.org/ecma-262/6.0/#sec-white-space (como se vincula a partir de la respuesta de Felix King) llama específicamente a U + FEFF para ser considerado espacio en blanco en el código fuente JS. U + FFFE no está en la lista, pero eso me parece un error de omisión.
zwol
1
@zwol, no es un error de omisión, porque no hay un carácter U + FFFE. Tratarlo como un espacio en blanco es un error. De hecho, tratarlo como un personaje válido es un error en la mayoría de los casos. U + 0085 no es un espacio en blanco según el espectro JS, pero esa especificación que requiere una carcasa especial de U + 0085 para que no sea una nueva línea es extraña y podría decirse que es un error en la especificación.
Jon Hanna
56

Parece que el carácter que está utilizando es más largo que el signo menos real (un guión).

 
-

La parte superior es lo que está utilizando, la parte inferior es lo que debería ser el signo menos. Parece que ya lo sabes, así que ahora veamos por qué Javascript hace esto.

El carácter que usa es en realidad la marca de espacio ogham, que es un carácter de espacio en blanco, por lo que básicamente se interpreta como lo mismo que un espacio, lo que significa que su declaración se parece alert(2+ 40)a Javascript.

Hay otros caracteres como este en Javascript. Puedes ver una lista completa aquí en Wikipedia .


Algo interesante que noté sobre este personaje es la forma en que Google Chrome (y otros posibles navegadores) lo interpreta en la barra superior de la página.

ingrese la descripción de la imagen aquí

Es un bloque con 1680adentro. Ese es en realidad el número unicode para la marca de espacio ogham. Parece que solo mi máquina está haciendo esto, pero es algo extraño.


Decidí probar esto en otros idiomas para ver qué pasa y estos son los resultados que obtuve.


Idiomas en los que no funciona:

Python 2 y 3

>> 2+ 40
  File "<stdin>", line 1
    2+ 40
        ^
SyntaxError: invalid character in identifier

Rubí

>> 2+ 40
NameError: undefined local variable or method ` 40' for main:Object
    from (irb):1
    from /home/michaelpri/.rbenv/versions/2.2.2/bin/irb:11:in `<main>'

Java (dentro del mainmétodo)

>> System.out.println(2+ 40);
Main.java:3: error: illegal character: \5760
            System.out.println(2+?40);
                                 ^
Main.java:3: error: ';' expected
            System.out.println(2+?40);
                                  ^
Main.java:3: error: illegal start of expression
            System.out.println(2+?40);
                                    ^
3 errors

PHP

>> 2+ 40;
Use of undefined constant  40 - assumed ' 40' :1

C

>> 2+ 40
main.c:1:1: error: expected identifier or '(' before numeric constant
 2+ 40
 ^
main.c:1:1: error: stray '\341' in program
main.c:1:1: error: stray '\232' in program
main.c:1:1: error: stray '\200' in program

exit status 1

Vamos

>> 2+ 40
can't load package: package .: 
main.go:1:1: expected 'package', found 'INT' 2
main.go:1:3: illegal character U+1680

exit status 1

Perl 5

>> perl -e'2+ 40'                                                                                                                                   
Unrecognized character \xE1; marked by <-- HERE after 2+<-- HERE near column 3 at -e line 1.

Idiomas en los que funciona:

Esquema

>> (+ 240)
=> 42

C # (dentro del Main()método)

Console.WriteLine(2+ 40);

Output: 42

Perl 6

>> ./perl6 -e'say 2+ 40' 
42
michaelpri
fuente
34
Ubuntu no es el problema. La fuente del título de la ventana que está utilizando es.
PSkocik
2
firefox (iceweasel) y google chrome en debian parecen mostrar muy bien el carácter unicode, aunque he hecho todo lo posible para garantizar la compatibilidad unicode en mi sistema. (en realidad, lo más útil que hice fue lo más simple: sudo apt-get install unicodeaunque solo después de horas de investigación e intentos fallidos)
sig_seg_v
@PSkocik Interesante, he tenido problemas con las fuentes aquí antes, por lo que probablemente sea probable
michaelpri
51
@PSkocik “Ubuntu no es el problema. La fuente del título de la ventana que está utilizando es ". ... que es " Ubuntu ".
user4642212
1
@PSkocik Finalmente lo arreglé :) Solo necesitaba cambiar la fuente de la barra de título del sistema.
michaelpri
43

Supongo que tiene que ver algo con el hecho de que por alguna extraña razón se clasifica como espacio en blanco:

$ unicode  
U+1680 OGHAM SPACE MARK
UTF-8: e1 9a 80  UTF-16BE: 1680  Decimal: &#5760;( )
Uppercase: U+1680
Category: Zs (Separator, Space)
Bidi: WS (Whitespace)
PSkocik
fuente
Si se trata de copiar y pegar desde su terminal, me gustaría saber dónde encontró el comando unicode.
BenjiWiebe
16
Es del paquete de Ubuntu llamado (espera ...) unicodepor Radovan Garabík. El repositorio correspondiente está en github.com/garabik/unicode .
PSkocik
OK, gracias por el enlace de github. AFAICT, no está en los repositorios de Fedora.
BenjiWiebe
@PSkocik ' '.codePointAt(0)en la consola producirá 5760. ahora google 5760 unicode.
Royi Namir
6

También me gustaría saber si hay más personajes que se comportan así.

Me parece recordar haber leído un artículo hace un tiempo sobre el reemplazo travieso de punto y coma (U + 003B) en el código de alguien con U + 037E, que es el signo de interrogación griego.

Ambos se ven iguales (en la medida en que creo que los griegos mismos usan U + 003B), pero este artículo declaró que el otro no funcionaría.

Puede encontrar más información sobre esto en Wikipedia aquí: https://en.wikipedia.org/wiki/Question_mark#Greek_question_mark

Y una pregunta (cerrada) sobre el uso de esto como una broma del propio SO. No es donde originalmente lo leí AFAIR: JavaScript Prank / Joke

mediodía
fuente