Dlaczego 2+ 40 równa się 42?

Byłem zdumiony, gdy kolega pokazał mi tę linię alarmowania JavaScript 42.

alert(2+ 40);

Szybko okazuje się, że to, co wygląda jak znak minus, jest tajemnym znakiem Unicode o wyraźnie odmiennej semantyce.

To sprawiło, że zastanawiałem się, dlaczego ten znak nie generuje błędu składni podczas parsowania wyrażenia. Chciałbym też wiedzieć, czy jest więcej postaci zachowujących się w ten sposób.

Author: GOTO 0, 2015-07-19

5 answers

Ten znak to "znak przestrzeni OGHAM" , który jest znakiem spacji. Więc kod jest odpowiednikiem alert(2+ 40).

Chciałbym też wiedzieć, czy jest więcej postaci zachowujących się w ten sposób.

Dowolny znak Unicode w klasie ZS jest białym znakiem spacji w JavaScript, ale nie wydaje się być tak wiele.

Jednak JavaScript pozwala również na użycie znaków Unicode w identyfikatorach , co pozwala na użycie interesującej zmiennej nazwy typu ಠ_ಠ.

 470
Author: Felix Kling,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-26 00:26:56

Po przeczytaniu pozostałych odpowiedzi napisałem prosty skrypt, aby znaleźć wszystkie znaki Unicode w zakresie U + 0000-u + FFFF, które zachowują się jak białe spacje. Jak się wydaje, jest ich 26 lub 27 w zależności od przeglądarki, z nieporozumieniami na temat U + 0085 i U + FFFE.

Zauważ, że większość z tych znaków wygląda jak zwykła biała spacja.

function isSpace(ch)
{
    try
    {
        return Function('return 2 +' + ch + ' 2')() === 4;
    }
    catch(e)
    {
        return false;
    }
}

for (var i = 0; i <= 0xffff; ++i)
{
    var ch = String.fromCharCode(i);
    if (isSpace(ch))
    {
        document.body.appendChild(document.createElement('DIV')).textContent = 'U+' + ('000' + i.toString(16).toUpperCase()).slice(-4) + '    "' + ch + '"';
    }
}
div { font-family: monospace; }
 81
Author: GOTO 0,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-20 13:33:47

Wygląda na to, że znak, którego używasz, jest dłuższy niż rzeczywisty znak minus (myślnik).

 
-

Góra jest tym, czego używasz, dół jest tym, co powinien być znak minus. Wydaje się, że już to wiesz, więc teraz zobaczmy, dlaczego Javascript to robi.

Znak, którego używasz, jest w rzeczywistości znacznikiem przestrzeni ogham, który jest białym znakiem spacji, więc jest zasadniczo interpretowany jako to samo, co spacja, co oznacza, że Twoje instrukcja wygląda jak alert(2+ 40) do Javascript.

Istnieją inne znaki tego typu w Javascript. Możesz zobaczyć pełną listę tutaj na Wikipedii .


Coś ciekawego zauważyłem w tej postaci jest sposób, w jaki Google Chrome (i Możliwe inne przeglądarki) interpretuje go w górnym pasku strony.

Tutaj wpisz opis obrazka

Jest to blok z 1680 wewnątrz niego. W rzeczywistości jest to numer unicode dla znaku przestrzeni ogham. Wygląda na to, że to tylko moje maszyna robi to, ale to dziwna rzecz.


Postanowiłem wypróbować to w innych językach, aby zobaczyć, co się stanie i oto wyniki, które otrzymałem.


Języki, w których nie działa:

Python 2 & 3

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

Ruby

>> 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 (wewnątrz metody main)

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

Idź

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

Języki, w których działa:

Schemat

>> (+ 2  40)
=> 42

C # (wewnątrz metody Main())

Console.WriteLine(2+ 40);

Output: 42

Perl 6

>> ./perl6 -e'say 2+ 40' 
42
 56
Author: michaelpri,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-20 09:12:55

Myślę, że to ma coś wspólnego z faktem, że z jakiegoś dziwnego powodu klasyfikuje się jako białe spacje:

$ 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)
 43
Author: PSkocik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-19 23:57:18
Chciałbym też wiedzieć, czy jest więcej postaci zachowujących się w ten sposób.

Wydaje mi się, że jakiś czas temu czytałem artykuł o złośliwym zastępowaniu półkolonów (U+003B) w czyimś kodzie przez U+037E, który jest greckim znakiem zapytania.

Oba wyglądają tak samo (do tego stopnia, że wierzę, że Grecy sami używają U + 003B), ale ten artykuł stwierdził, że drugi nie zadziała.

Więcej informacji na ten temat z Wikipedii znajduje się tutaj: https://en.wikipedia.org/wiki/Question_mark#Greek_question_mark

I (zamknięte) pytanie o wykorzystanie tego jako żart z samego SO. Nie tam, gdzie pierwotnie czytałem to AFAIR chociaż: JavaScript Prank / Joke

 6
Author: noonand,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:08