为什么2 + 40等于42?

GOT*_*O 0 360 javascript unicode

当一位同事向我展示这一行警告42时,我感到很困惑.

事实证明,看起来像减号的实际上是一个神秘的Unicode字符,具有明显不同的语义.

这让我想知道为什么该字符在解析表达式时不会产生语法错误.我还想知道是否有更多的人物表现得像这样.

Fel*_*ing 470

那个角色是"OGHAM SPACE MARK",这是一个空间角色.所以代码相当于alert(2+ 40).

我还想知道是否有更多的人物表现得像这样.

Zs类中的任何Unicode字符都是JavaScript中的空白字符,但似乎没有那么多.

但是,JavaScript还允许在标识符中使用Unicode字符,这使您可以使用有趣的变量名称?_?.

  • 当`ಠ_ಠ`可以用作JS中的标识符时我的反应:**ಠ_ಠ** (19认同)
  • @immibis这个答案的最后一部分是一个图像形式的图释,网址是http://www.disapprovallook.com/ (12认同)
  • 带有十六进制代码的盒子下划线框,带有十六进制代码.它意味着哪个角色? (3认同)
  • 请注意,在JavaScript中,不仅"Zs"字符被视为空格.还有更多:https://github.com/mathiasbynens/regexpu/blob/ff17a00b63a017a69fd93da455a8944eb18918ce/scripts/character-class-escape-sets.js#L69-L87 (3认同)
  • @ChrisCirefice强调被视为一封信长期存在于C风格的语言中."ಠ"被视为一封信只是常识,因为它是一封信.如果`ಠ_ಠ`不能用作标识符,那将是一个明显的错误. (2认同)

GOT*_*O 0 81

在阅读了其他答案之后,我写了一个简单的脚本来查找U + 0000-U + FFFF范围内的所有Unicode字符,其行为类似于空格.看起来,有26或27个取决于浏览器,对U + 0085和U + FFFE存在分歧.

请注意,大多数这些字符看起来像一个普通的空白区域.

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 + '"';
    }
}
Run Code Online (Sandbox Code Playgroud)
div { font-family: monospace; }
Run Code Online (Sandbox Code Playgroud)

  • U + 0085"NEL"被Unicode定义为空格,但有很长的错误处理历史.除了NChar之外,U + FFFE是一个没有名称且没有属性的非字符,不应被任何合理的空格视为空格.也就是说,我的浏览器在这两点上都不同意我:) (17认同)
  • @hobbs U + FFFE也是一个`\ p {Default Ignorable Code Point},而不仅仅是`\ p {Noncharacter Code Pount}.U + 0085一直是`\ p {Whitespace}`代码点.邪恶的是U + 180E MONGOLIAN VOWEL SEPARATOR,"最近"失去了它的'\ p {Whitespace}'财产.请注意,`\ p {Pattern Whitespace}`是一个小得多的集合,也是一个不可变的属性.但是`\ p {Whitespace}`不是. (4认同)
  • `FEFF`是BOM,可以像文本中的"零宽度不间断空间"一样对待.`FFFE`是它的endian交换等价物.也许这就是一些浏览器对待的原因是空白. (2认同)
  • @zwol,这不是遗漏的错误,因为没有字符U+FFFE。将其视为空白是​​一个错误。事实上,在大多数情况下,将其视为有效字符是一个错误。根据 JS 规范,U+0085 不是空白,但该规范要求 U+0085 的特殊外壳不是新行,这很奇怪,可以说是规范中的一个错误。 (2认同)

mic*_*pri 56

看来您使用的角色实际上比实际减号(连字符)更长.

?
-
Run Code Online (Sandbox Code Playgroud)

顶部是您正在使用的,底部是减号应该是什么.你似乎已经知道了,所以现在让我们看看为什么Javascript会这样做.

您使用的字符实际上是ogham空格标记,它是一个空白字符,因此它基本上被解释为与空格相同的东西,这意味着您的语句看起来像alert(2+ 40)Javascript.

在Javascript中还有其他类似的字符.您可以在维基百科上看到完整列表.


我注意到这个角色的有趣之处在于Google Chrome(以及可能的其他浏览器)在页面顶部栏中解释它的方式.

在此输入图像描述

它是一个1680内部的块.这实际上是ogham空间标记的unicode数字.它似乎只是我的机器这样做,但这是一个奇怪的事情.


我决定用其他语言尝试一下,看看会发生什么,这些都是我得到的结果.


语言不起作用:

Python 2和3

>> 2+?40
  File "<stdin>", line 1
    2+?40
        ^
SyntaxError: invalid character in identifier
Run Code Online (Sandbox Code Playgroud)

红宝石

>> 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>'
Run Code Online (Sandbox Code Playgroud)

Java(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
Run Code Online (Sandbox Code Playgroud)

PHP

>> 2+?40;
Use of undefined constant ?40 - assumed '?40' :1
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

>> 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
Run Code Online (Sandbox Code Playgroud)

Perl 5

>> perl -e'2+?40'                                                                                                                                   
Unrecognized character \xE1; marked by <-- HERE after 2+<-- HERE near column 3 at -e line 1.
Run Code Online (Sandbox Code Playgroud)

它的语言有效:

方案

>> (+ 2 ?40)
=> 42
Run Code Online (Sandbox Code Playgroud)

C# (Main()方法内)

Console.WriteLine(2+?40);

Output: 42
Run Code Online (Sandbox Code Playgroud)

Perl 6

>> ./perl6 -e'say 2+?40' 
42
Run Code Online (Sandbox Code Playgroud)

  • @PSkocik _"Ubuntu不是问题.你正在使用的窗口标题字体是."_ ...这是"[Ubuntu](http://font.ubuntu.com/)". (51认同)
  • Ubuntu不是问题.您正在使用的窗口标题字体是. (34认同)
  • firefox(iceweasel)和debian上的google chrome似乎显示unicode char就好了,虽然我已经不遗余力地确保我系统上的unicode兼容性.(实际上,我做的最有用的事情是最简单的:`sudo apt-get install unicode`,虽然只是经过数小时的研究和尝试失败) (2认同)

PSk*_*cik 43

我想它必须做一些事情,因为一些奇怪的原因它被归类为空格:

$ 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)
Run Code Online (Sandbox Code Playgroud)

  • 它来自RadovanGarabík命名的(等待它......)`unicode`的Ubuntu包.相应的仓库位于https://github.com/garabik/unicode. (16认同)

noo*_*and 6

我还想知道是否有更多的人物表现得像这样.

我似乎记得有一段时间读过一篇关于用U + 037E(希腊问号)恶意替换某人代码中的半冒号(U + 003B)的文章.

它们看起来都一样(我认为希腊人自己使用的是U + 003B),但是这篇文章说另一个不行.

有关维基百科的更多信息,请访问:https://en.wikipedia.org/wiki/Question_mark#Greek_question_mark

还有一个(封闭的)关于将其用作SO本身的恶作剧的问题.不是我最初读它AFAIR的地方: JavaScript恶作剧/笑话