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

警报(2+ 40);

很快就会发现,看起来像负号的东西实际上是一个神秘的Unicode字符,具有明显不同的语义。

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


该字符是“OGHAM SPACE MARK”,这是一个空格字符。所以代码相当于alert(2+ 40)。

我还想知道是否有更多的角色有这样的行为。

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

然而,JavaScript也允许在标识符中使用Unicode字符,这允许您使用有趣的变量名,如ಠ_ಠ。


我猜这和它被归类为空白的原因有关:

$ unicode  
U+1680 OGHAM SPACE MARK
UTF-8: e1 9a 80  UTF-16BE: 1680  Decimal:  
  ( )
Uppercase: U+1680
Category: Zs (Separator, Space)
Bidi: WS (Whitespace)

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

 
-

上面是你要用的,下面是负号。你似乎已经知道了这一点,所以现在让我们看看Javascript为什么这样做。

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

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


有趣的是,我注意到谷歌Chrome浏览器(可能还有其他浏览器)在页面顶部栏解释它的方式。

这是一个里面有1680人的街区。这实际上是unicode中ogham空格符号的编号。这似乎只是我的机器在做这件事,但这是一件奇怪的事情。


我决定在其他语言中尝试这个方法,看看会发生什么,下面是我得到的结果。


无法使用的语言:

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(在主方法内部)

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

它适用的语言:

计划

>> (+ 2  40)
=> 42

c# (在Main()方法内)

Console.WriteLine(2+ 40);

Output: 42

Perl 6

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

在阅读了其他答案后,我编写了一个简单的脚本,以查找U+ 0000-U +FFFF范围内所有表现为空格的Unicode字符。看起来,根据浏览器的不同,有26或27个,对U+0085和U+FFFE存在分歧。

注意,这些字符中的大多数看起来就像一个普通的空白。

function isSpace (ch) 可不, 我会用 可不, return Function(‘return 2 +’+ ch +‘2’)()= = = 4; 出于美观 于我(e) 可不, return false; 出于美观 出于美观 对于(vari=0;i <= 0xff;+ + i) 可不, varch=string.fromcharcode (i) (isSpace (ch)真有极乐世界) 可不, document.body.appendChild (document.createElement(‘dave’))。textContent =‘U + +(’‘万+ i.toString’(16).toUpperCase ()) .slice (-4 ) + ' "' + ch +’‘;” 出于美观 出于美观 font-family: monospace;出于美观


我还想知道是否有更多的角色有这样的行为。

我似乎记得前阵子读过一篇文章,说有人恶作剧地用希腊问号U+037E替换代码中的分号(U+003B)。

它们看起来是一样的(在某种程度上,我相信希腊人自己使用U+003B),但这篇文章指出,另一个不合适。

更多关于这方面的信息,维基百科在这里:https://en.wikipedia.org/wiki/Question_mark#Greek_question_mark

还有一个(封闭的)关于使用这个作为恶作剧的问题。不是我最初读到它的地方。 JavaScript恶作剧/笑话


许多语言不会编译这个表达式,但我很好奇Rust的编译器在这个主题上说了什么。它是出了名的严格,但经常会给我们知识和智慧与慈爱。

所以我让它编译这个:

fn main() {
    println!("{}", (2+ 40));
}

编译器回答说:

error: unknown start of token: \u{1680}
  |
  |     println!("{}", (2+ 40));
  |                       ^
  |
help: Unicode character ' ' (Ogham Space mark) looks like ' ' (Space), but it is not

另一方面,JavaScript(使用当前最新和最常用的浏览器进行测试)似乎对该字符相当冷淡,并简单地忽略它。