Por que 2 + 40 é igual a 42?

360

Fiquei perplexo quando um colega me mostrou essa linha de JavaScript alertando 42.

alert(2+ 40);

Acontece rapidamente que o que parece ser um sinal de menos é realmente um caractere Unicode arcano com semântica claramente diferente.

Isso me deixou pensando por que esse caractere não produz um erro de sintaxe quando a expressão é analisada. Eu também gostaria de saber se há mais personagens se comportando assim.

GOTO 0
fonte
28
@Elyasin Você copiou / colou ou redigitou?
user253751
4
Isso funciona no Visual C # também. Ao colar o caractere estranho no IDE do Visual Studio ou ao concluir a instrução digitando ;, o editor tende a alterar o caractere `` estranho para um espaço normal, mas se você desfazer essa "correção automática", tem o mesmo comportamento . Esse caractere tem a mesma semântica que um espaço, mesmo que pareça um hífen ou menos (em fontes comuns).
Jeppe Stig Nielsen
4
O oposto também pode acontecer. Alguns idiomas que suportam unicode em identificadores aceitam caracteres unicode que parecem espaço em branco (em outras palavras, você não pode vê-los); pode até ser possível ter identificadores completamente invisíveis.
gnasher729
58
(OT) Porque 42 é uma resposta para tudo?
ivan_pozdeev
4
@Thomas o fato de que o resultado inesperado foi causado por esse caractere Unicode já estava claro.
GOTO 0

Respostas:

470

Esse personagem é "OGHAM SPACE MARK" , que é um caractere de espaço. Portanto, o código é equivalente a alert(2+ 40).

Eu também gostaria de saber se há mais personagens se comportando assim.

Qualquer caractere Unicode na classe Zs é um caractere de espaço em branco no JavaScript , mas parece não haver muitos .

No entanto, o JavaScript também permite caracteres Unicode nos identificadores , o que permite usar nomes de variáveis ​​interessantes como ಠ_ಠ.

Felix Kling
fonte
3
Caixa com um código hexadecimal sublinhado caixa com um código hexadecimal. Qual personagem ele deveria ser?
user253751
12
@immibis A última parte desta resposta é um emoticon disponível em forma de imagem em disapprovallook.com
Mark S.
3
Observe que não apenas Zscaracteres são considerados espaços em branco no JavaScript. Existem mais: github.com/mathiasbynens/regexpu/blob/…
Mathias Bynens
20
Minha reação quando ಠ_ಠpode ser usado como um identificador em JS: #
Chris Cirefice
2
O sublinhado @ChrisCirefice sendo tratado como uma carta é antigo nos idiomas C. ser tratado como uma carta é apenas senso comum, já que é uma carta. Seria um erro claro se ಠ_ಠnão pudesse ser usado como um identificador.
Jon Hanna
81

Depois de ler as outras respostas, escrevi um script simples para encontrar todos os caracteres Unicode no intervalo U + 0000 – U + FFFF que se comportam como espaços em branco. Como parece, existem 26 ou 27 deles, dependendo do navegador, com divergências sobre o U + 0085 e o U + FFFE.

Observe que a maioria desses caracteres parece um espaço em branco comum.

GOTO 0
fonte
17
U + 0085 "NEL" é definido como espaço em branco pelo Unicode, mas tem um longo histórico de uso indevido. U + FFFE é um não caractere sem nome e sem propriedades além do NChar e não deve ser considerado espaço em branco por qualquer coisa razoável. Dito isto, meus discorda do navegador com me nos dois pontos :)
hobbs
4
@hobbs U + FFFE também é um \p{Default Ignorable Code Point}, não apenas um \p{Noncharacter Code Pount}. U + 0085 sempre foi um \p{Whitespace}ponto de código. O maligno é o separador de vozes da Mongólia U + 180E, que "recentemente" perdeu suas \p{Whitespace}propriedades. Observe que \p{Pattern Whitespace}é um conjunto muito menor e uma propriedade imutável. Mas \p{Whitespace}não é.
21415 Tchrist
2
FEFFé a lista técnica e pode ser tratada como um "espaço sem quebra de largura zero" nos textos. FFFEé o equivalente trocado endian. Talvez essa seja a razão pela qual alguns navegadores tratam seja o espaço em branco.
CodesInChaos
ecma-international.org/ecma-262/6.0/#sec-white-space (como vinculado na resposta de Felix King) especificamente chama U + FEFF para ser considerado espaço em branco no código-fonte JS. U + FFFE não está listado, mas isso me parece um erro de omissão.
Zwol
11
@zwol, não é um erro de omissão, porque não há caractere U + FFFE. Tratar como espaço em branco é um bug. De fato, tratá-lo como um personagem válido é um bug na maioria dos casos. O U + 0085 não é um espaço em branco de acordo com o JS JS, mas essas especificações exigem que o revestimento especial do U + 0085 não seja uma nova linha é bizarro e, sem dúvida, um bug na especificação.
21915 Jon Hanna
56

Parece que o caractere que você está usando é realmente maior do que o sinal de menos real (um hífen).

 
-

A parte superior é o que você está usando, a parte inferior é o que o sinal de menos deve ser. Você já sabe disso, então agora vamos ver por que o Javascript faz isso.

O caractere que você usa é, na verdade, a marca de espaço ogham, que é um caractere de espaço em branco; portanto, é basicamente interpretado como a mesma coisa que um espaço, o que significa que sua declaração se parece comalert(2+ 40) com Javascript.

Existem outros caracteres como este em Javascript. Você pode ver uma lista completa aqui na Wikipedia .


Algo interessante que notei sobre esse personagem é a maneira como o Google Chrome (e possíveis outros navegadores) o interpreta na barra superior da página.

insira a descrição da imagem aqui

É um bloco com 1680dentro dele. Na verdade, esse é o número unicode da marca de espaço ogham. Parece ser apenas minha máquina fazendo isso, mas é uma coisa estranha.


Decidi tentar isso em outros idiomas para ver o que acontece e esses são os resultados que obtive.


Idiomas em que não funciona:

Python 2 e 3

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

Rubi

>> 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 do 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

Vai

>> 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 em que trabalha:

Esquema

>> (+ 240)
=> 42

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

Console.WriteLine(2+ 40);

Output: 42

Perl 6

>> ./perl6 -e'say 2+ 40' 
42
michaelpri
fonte
34
Ubuntu não é o problema. A fonte do título da janela que você está usando é.
PSKocik
2
O firefox (iceweasel) e o google chrome no debian parecem exibir muito bem o caractere unicode, embora eu tenha me esforçado bastante para garantir a compatibilidade unicode no meu sistema. (na verdade, a coisa mais útil que eu fiz foi a mais simples: sudo apt-get install unicode, embora só depois de horas de pesquisa e tentativas falhadas)
sig_seg_v
@PSkocik Interessante, eu tive problemas de fonte aqui antes, de modo que é provavelmente provável
michaelpri
51
@PSkocik “Ubuntu não é o problema. A fonte do título da janela que você está usando é. ... que é o " Ubuntu ".
user4642212
11
@PSkocik eu finalmente consertei :) Só precisava mudar a fonte da barra de título do sistema.
24515 michaelpri
43

Eu acho que tem a ver com o fato de que, por algum motivo estranho, classifica como espaço em branco:

$ 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
fonte
Se for copiar e colar no seu terminal, gostaria de saber onde você encontrou o comando unicode.
BenjiWiebe
16
É do pacote Ubuntu chamado (aguarde ...) unicodepor Radovan Garabík. O repo correspondente está em github.com/garabik/unicode .
PSKocik
OK, obrigado pelo link do github. AFAICT, não está nos repositórios do Fedora.
BenjiWiebe
O @PSkocik ' '.codePointAt(0)no console renderá 5760. agora google 5760 unicode.
Royi Namir 22/07/2015
6

Eu também gostaria de saber se há mais personagens se comportando assim.

Parece que me lembro de ler um artigo sobre a substituição maliciosa de ponto e vírgula (U + 003B) no código de alguém por U + 037E, que é o ponto de interrogação grego.

Ambos têm a mesma aparência (na medida em que acredito que os próprios gregos usam U + 003B), mas este artigo afirmou que o outro não funcionaria.

Mais algumas informações sobre isso na Wikipedia estão aqui: https://en.wikipedia.org/wiki/Question_mark#Greek_question_mark

E uma pergunta (fechada) sobre como usar isso como brincadeira do próprio SO. Não é onde eu originalmente li AFAIR: JavaScript Prank / Joke

meio-dia
fonte