为什么2+ 40等于42?

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

.
alert(2+ 40);

It quickly turns out that what looks like a minus sign is actually an arcane Unicode character with clearly different semantics.

This left me wondering why that character doesn't produce a syntax error when the expression is parsed. I'd also like to know if there are more characters behaving like this.

34239 次浏览

这个字符是“欧甘空格标记”,它是一个空格字符。所以代码等价于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为什么这样做。

你使用的字符实际上是奥甘空格符号,它是一个空白字符,所以它基本上被解释为一个空格,这意味着你的语句看起来像Javascript的alert(2+ 40)

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


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

enter image description here

它是一个包含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(在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

>> 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)
{
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; }

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

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

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

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

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

许多语言不会编译这个表达式,但我很好奇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(使用当前最新和最常用的浏览器进行测试)似乎对该字符相当冷淡,并简单地忽略它。