为什么2+ 40等于42?


360

当一位同事向我展示这一行JavaScript警报42时,我感到困惑。

alert(2+ 40);

很快就发现,看起来像减号的实际上是具有明显不同语义的奥秘Unicode字符。

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


28
@Elyasin您复制/粘贴或重新键入吗?
user253751

4
这也适用于Visual C#。当将奇怪的字符粘贴到Visual Studio IDE中或通过键入来完成语句时;,编辑器倾向于将奇怪的``字符更改为普通空间,但是如果撤消该“自动更正”,则具有相同的行为。 。即使看起来像连字符或减号(使用常规字体),该字符也具有与空格相同的语义。
杰普·斯蒂格·尼尔森

4
相反也可能发生。某些在标识符中支持unicode的语言会接受看起来像空白的unicode字符(换句话说,您看不到它们)。甚至可能有完全不可见的标识符。
gnasher729

58
(OT)因为42是一切
ivan_pozdeev

4
@Thomas意外结果是由该Unicode字符引起的事实已经很清楚。
GOTO

Answers:


470

该字符是“ OGHAM SPACE MARK”,这是一个空格字符。因此,代码等同于alert(2+ 40)

我还想知道是否还有更多这样的字符。

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

但是,JavaScript还允许标识符中使用Unicode字符,这使您可以使用有趣的变量名,例如ಠ_ಠ


3
带十六进制代码的框下划线带十六进制代码的框下划线。这是什么角色?
user253751

12
@immibis该答案的最后一部分是可在disapprovallook.com上
Mark

3
请注意,ZsJavaScript中不仅将字符视为空格。还有更多:github.com/mathiasbynens/regexpu/blob/...
马蒂亚斯Bynens

20
我的反应时ಠ_ಠ,可以使用如在JS的标识符:ಠ_ಠ
克里斯Cirefice

2
@ChrisCirefice下划线在C样式语言中长期存在,被视为字母。被视为字母只是常识,因为它是字母。如果ಠ_ಠ不能用作标识符,那将是一个明显的错误。
乔恩·汉娜

81

阅读完其他答案后,我写了一个简单的脚本来查找U + 0000–U + FFFF范围内的所有Unicode字符,它们的行为类似于空格。看起来,取决于浏览器,其中有26或27个,但对U + 0085和U + FFFE的意见不同。

请注意,大多数这些字符看起来就像是常规空格。


17
U + 0085“ NEL”由Unicode定义为空格,但是长期以来一直被错误处理。U + FFFE是一个非字符,除了NChar外,没有名称,没有任何属性,因此在任何合理的情况下都不应将其视为空格。就是说,我的浏览器在两点上都不同意我的观点:)
hobbs 2015年

4
@hobbs U + FFFE也是一个\p{Default Ignorable Code Point},而不仅仅是一个\p{Noncharacter Code Pount}。U + 0085一直是\p{Whitespace}代码点。邪恶的是U + 180E蒙古腔分隔器,“最近”失去了它的\p{Whitespace}财产。请注意,这\p{Pattern Whitespace}是一个更小的集合,并且是不可变的属性。但是\p{Whitespace}不是。
tchrist

2
FEFF是BOM,可以将其视为文本中的“零宽度不间断空格”。FFFE它是字节序互换的。也许这就是某些浏览器将其视为空白的原因。
CodesInChaos

ecma-international.org/ecma-262/6.0/#sec-white-space(根据Felix King的回答链接)明确指出U + FEFF在JS源代码中被视为空白。U + FFFE并未列出,但这使我感到疏忽。
zwol

1
@zwol,这不是遗漏错误,因为没有字符U + FFFE。将其视为空白是​​一个错误。确实,在大多数情况下,将其视为有效字符是一个错误。根据JS规范,U + 0085并非空白,但是该规范要求U + 0085的特殊包装不能成为新的行,这是奇怪的,并且可以说是规范中的一个错误。
乔恩·汉娜

56

看来您正在使用的字符实际上比实际的负号(连字符)更长。

 
-

顶部是您正在使用的内容,底部是负号应该是什么。您似乎确实已经知道这一点,所以现在让我们看看为什么Javascript会这样做。

您使用的字符实际上是ogham空格标记,它是一个空格字符,因此它基本上被解释为与空格相同,这意味着您的语句类似于alert(2+ 40)Javascript。

Javascript中还有其他类似的字符。您可以在Wikipedia上查看完整列表。


我注意到该字符的有趣之处在于Google Chrome(和其他可能的浏览器)在页面顶部栏中解释该字符的方式。

在此处输入图片说明

这是一个1680内部的块。这实际上是ogham空格标记的unicode号。看来这只是我的机器在做,但是这很奇怪。


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


无法使用的语言:

Python 2和3

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

红宝石

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

Javamain方法内)

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

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

它可以使用的语言:

方案

>> (+ 240)
=> 42

C#Main()方法内)

Console.WriteLine(2+ 40);

Output: 42

Perl 6

>> ./perl6 -e'say 2+ 40' 
42

34
Ubuntu不是问题。您正在使用的窗口标题字体是。
PSkocik

2
尽管我已尽力确保unicode兼容我的系统,但在debian上使用firefox(iceweasel)和google chrome似乎可以很好地显示unicode char。(实际上,我做过的最有用的事情是最简单的方法:sudo apt-get install unicode,尽管只是经过数小时的研究和失败的尝试)
sig_seg_v 2015年

@PSkocik有趣的是,我之前在这里遇到过字体问题,所以很有可能
michaelpri

51
@PSkocik “ Ubuntu不是问题。您正在使用的窗口标题字体是。” …这是“ Ubuntu ”。
user4642212

1
@PSkocik我终于修复了它:)只需要更改系统标题栏字体。
michaelpri

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)

如果您是从终端复制粘贴的内容,我想知道您在哪里找到了命令unicode
BenjiWiebe

16
它来自unicodeRadovanGarabík 命名为(waiting it ...)的Ubuntu软件包。相应的仓库位于github.com/garabik/unicode
PSkocik

好的,谢谢github链接。AFAICT,它不在Fedora仓库中。
BenjiWiebe

' '.codePointAt(0)控制台上的@PSkocik 将产生5760。现在是Google 5760 unicode。
罗伊·纳米尔

6

我还想知道是否还有更多这样的字符。

我似乎记得有一段时间读过一篇文章,内容是用希腊问号U + 037E巧妙地替换了某人代码中的分号(U + 003B)。

它们看上去都一样(就我所相信的希腊人而言,他们使用的是U + 003B),但本文指出,另一种则行不通。

来自Wikipedia的一些更多信息在这里: https //en.wikipedia.org/wiki/Question_mark#Greek_question_mark

关于将其用作SO本身的恶作剧的一个(封闭的)问题。虽然不是我最初阅读的地方: JavaScript恶作剧/开玩笑

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.