web-dev-qa-db-fra.com

Pourquoi 2+ 40 est-il égal à 42?

J'ai été perplexe lorsqu'un collègue m'a montré cette ligne d'alerte JavaScript 42.

alert(2+ 40);

Il s'avère rapidement que ce qui ressemble à un signe moins est en réalité un caractère Unicode arcanique avec une sémantique clairement différente.

Cela m'a laissé se demander pourquoi ce caractère ne produisait pas d'erreur de syntaxe lorsque l'expression est analysée. J'aimerais aussi savoir s'il y a plus de personnages se comportant de la sorte.

357
GOTO 0

Ce caractère est "OGHAM SPACE MARK" , qui est un caractère d'espace. Donc, le code est équivalent à alert(2+ 40).

J'aimerais aussi savoir s'il y a plus de personnages se comportant de la sorte.

Tout caractère Unicode de la classe Zs est un caractère d’espace blanc en JavaScript , mais il ne semble pas y en avoir autant .

Cependant, JavaScript autorise également les caractères Unicode dans les identificateurs , ce qui vous permet d'utiliser des noms de variables intéressants tels que ಠ_ಠ.

467
Felix Kling

Après avoir lu les autres réponses, j’ai écrit un script simple pour trouver tous les caractères Unicode de la plage U + 0000 – U + FFFF qui se comportent comme des espaces blancs. Selon les navigateurs, ils sont au nombre de 26 ou 27, avec des désaccords sur U + 0085 et U + FFFE.

Notez que la plupart de ces caractères ressemblent à des espaces blancs normaux.

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

Il semble que le caractère que vous utilisez soit en réalité plus long que le signe moins (un trait d'union).

 
-

Le haut est ce que vous utilisez, le bas est ce que le signe moins devrait être. Vous semblez déjà le savoir, voyons maintenant pourquoi Javascript le fait.

Le caractère que vous utilisez est en fait le ogham space mark qui est un caractère d'espacement, il est donc interprété de la même manière qu'un espace, ce qui signifie que votre instruction ressemble à alert(2+ 40) Javascript.

Il y a d'autres personnages comme celui-ci en Javascript. Vous pouvez voir une liste complète ici sur Wikipedia .


Quelque chose d’intéressant, j’ai remarqué à propos de ce personnage, c’est la façon dont Google Chrome (et d’autres navigateurs possibles) l’interprète dans la barre supérieure de la page.

enter image description here

C'est un bloc avec 1680 à l'intérieur de celui-ci. C'est en fait le numéro unicode de la marque d'espace ogham. Cela semble être juste ma machine, mais c'est une chose étrange.


J'ai décidé d'essayer cela dans d'autres langues pour voir ce qui se passe et ce sont les résultats que j'ai obtenus.


Langues dans lesquelles il ne fonctionne pas:

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 (dans la méthode 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

Go

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

Langues dans lesquelles cela fonctionne:

Schéma

>> (+ 2  40)
=> 42

C # (à l'intérieur de la méthode Main())

Console.WriteLine(2+ 40);

Output: 42

Perl 6

>> ./Perl6 -e'say 2+ 40' 
42
56
michaelpri

Je suppose que cela doit faire quelque chose avec le fait que, pour une raison étrange, il est classé dans les espaces:

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

J'aimerais aussi savoir s'il y a plus de personnages se comportant de la sorte.

Il me semble me rappeler avoir lu un article il y a quelque temps sur le remplacement malicieux de points-virgules (U + 003B) dans le code de quelqu'un par U + 037E, qui est le point d'interrogation grec.

Ils se ressemblent tous les deux (dans la mesure où je pense que les Grecs utilisent eux-mêmes U + 003B), mais cet article indiquait que l’autre ne fonctionnerait pas.

Quelques informations supplémentaires à ce sujet sur Wikipedia sont ici: https://en.wikipedia.org/wiki/Question_mark#Greek_question_mark

Et une question (fermée) sur l'utilisation de ceci comme farce de SO lui-même. Pas là où je l’ai lu au départ, cependant: JavaScript Prank/Joke

6
noonand