题 为什么++ [[]] [+ []] + [+ []]返回字符串“10”?


这是有效的并返回字符串 "10" 在JavaScript中(这里有更多例子):

++[[]][+[]]+[+[]]

为什么?这里发生了什么?


1440
2017-08-26 08:56


起源


首先要了解这一点 +[] 将一个空数组转换为 0......然后浪费一个下午...;) - deceze♦
有关 stackoverflow.com/questions/4170978/explain-why-this-works 。 - Juho Vepsäläinen
typeof ++ [[]] [+ []] + [+ []]在Chrome控制台中返回“number0”。 - dgo
“这个世界上有++ [[]] [+ []] + [+ []]种类型的人......”衬衫迫在眉睫。 - dmanexe
您可以使用将js代码转换为此代码 jsfuck.com - Oss


答案:


如果我们把它分开,这个混乱就等于:

++[[]][+[]]
+
[+[]]

在JavaScript中,确实如此 +[] === 0+ 将某些东西转换为数字,在这种情况下,它将归结为 +"" 要么 0 (见下面的规格详情)。

因此,我们可以简化它(++ 优先于 +):

++[[]][0]
+
[0]

因为 [[]][0] 意思是:从中获取第一个元素 [[]],确实如此:

  • [[]][0] 返回内部数组([])。由于参考,说错了 [[]][0] === [],但是让我们调用内部数组 A 避免错误的表示法。
  • ++[[]][0] == A + 1从那以后 ++ 意思是'递增1'。
  • ++[[]][0] === +(A + 1);换句话说,它总是一个数字(+1 不一定会返回一个数字,而 ++ 总是这样 - 感谢Tim Down指出这一点。

同样,我们可以将混乱简化为更清晰的内容。让我们替代 [] 回来 A

+([] + 1)
+
[0]

在JavaScript中,这也是如此: [] + 1 === "1"因为 [] == "" (加入一个空数组),所以:

  • +([] + 1) === +("" + 1),和
  • +("" + 1) === +("1"),和
  • +("1") === 1

让我们进一步简化它:

1
+
[0]

此外,在JavaScript中也是如此: [0] == "0",因为它正在使用一个元素加入一个数组。连接将连接由分隔的元素 ,。使用一个元素,您可以推断出该逻辑将导致第一个元素本身。

所以,最后我们得到(number + string = string):

1
+
"0"

=== "10" // Yay!

规格细节 +[]

这是一个相当迷宫,但要做 +[],首先它被转换为字符串,因为那是什么 + 说:

11.4.6一元+算子

一元+运算符将其操作数转换为数字类型。

生产UnaryExpression:+ UnaryExpression的计算方法如下:

  1. 设expr是评估UnaryExpression的结果。

  2. 返回ToNumber(GetValue(expr))。

ToNumber() 说:

目的

应用以下步骤:

  1. 设primValue为ToPrimitive(输入参数,提示字符串)。

  2. 返回ToString(primValue)。

ToPrimitive() 说:

目的

返回Object的默认值。通过调用对象的[[DefaultValue]]内部方法,传递可选提示PreferredType来检索对象的默认值。对于8.12.8中的所有本机ECMAScript对象,此规范定义了[[DefaultValue]]内部方法的行为。

[[DefaultValue]] 说:

8.12.8 [[DefaultValue]](提示)

当使用提示字符串调用O的[[DefaultValue]]内部方法时,将执行以下步骤:

  1. 设toString是使用参数“toString”调用对象O的[[Get]]内部方法的结果。

  2. 如果IsCallable(toString)为真,那么,

一个。令str为调用toString的[[Call]]内部方法的结果,其中O为此值,且为空参数列表。

湾如果str是原始值,则返回str。

.toString 一个数组说:

15.4.4.2 Array.prototype.toString()

调用toString方法时,将执行以下步骤:

  1. 令数组是在此值上调用ToObject的结果。

  2. 让func成为使用参数“join”调用数组的[[Get]]内部方法的结果。

  3. 如果IsCallable(func)为false,则让func成为标准的内置方法Object.prototype.toString(15.2.4.2)。

  4. 返回调用func提供数组的[[Call]]内部方法的结果作为此值和空参数列表。

所以 +[] 归结为 +""因为 [].join() === ""

再一次, + 定义为:

11.4.6一元+算子

一元+运算符将其操作数转换为数字类型。

生产UnaryExpression:+ UnaryExpression的计算方法如下:

  1. 设expr是评估UnaryExpression的结果。

  2. 返回ToNumber(GetValue(expr))。

ToNumber 定义为 "" 如:

StringNumericLiteral ::: [empty]的MV为0。

所以 +"" === 0, 因此 +[] === 0


1876
2017-08-26 08:58



@harper:这是严格的相等检查器,即它只返回 true 如果值和类型都相同。 0 == "" 回报 true (类型转换后相同),但是 0 === "" 是 false (不是同一类型)。 - pimvdb
部分原因是不正确的。表达归结为 1 + [0]不是 "1" + [0],因为前缀(++)运算符总是返回一个数字。看到 bclary.com/2004/11/07/#a-11.4.4 - Tim Down
@Tim Down:你完全正确。我正在努力纠正这个问题,但在尝试这样做的时候,我发现了别的东西。我不确定这是怎么回事。 ++[[]][0] 确实回来了 1但是 ++[] 抛出错误。这很显着,因为它看起来像 ++[[]][0] 归结为 ++[]。你也许知道为什么 ++[] 抛出一个错误,而 ++[[]][0] 才不是? - pimvdb
@pimvdb:我很确定问题在于 PutValue 在前缀操作中调用(在ES3术语中,8.7.2)。 PutValue 需要参考,而 [] 作为表达式本身并不产生参考。包含变量引用的表达式(比如我们之前定义的那个) var a = [] 然后 ++a 工作)或对象的属性访问(例如 [[]][0])产生一个参考。简单来说,前缀运算符不仅会生成一个值,还需要某个位置来放置该值。 - Tim Down
@pimvdb:执行后就这样了 var a = []; ++a, a 是1.执行后 ++[[]][0],由数组创建的 [[]] 表达式现在只包含索引0处的数字1。 ++ 需要参考才能执行此操作。 - Tim Down


++[[]][+[]] => 1 // [+[]] = [0], ++0 = 1
[+[]] => [0]

然后我们有一个字符串连接

1+[0].toString() = 10

99
2017-09-14 13:54



希望所有答案都直截了当。 - Adam


以下内容改编自a 博客文章 在这个问题仍然关闭时回答了我发布的这个问题。链接是ECMAScript 3规范的(HTML副本),仍然是当今常用Web浏览器中JavaScript的基线。

首先,评论:这种表达式永远不会出现在任何(理智的)生产环境中,并且只是作为一种练习用于读者如何知道JavaScript的脏边缘。 JavaScript运算符在类型之间隐式转换的一般原则是有用的,一些常见的转换也是如此,但在这种情况下的大部分细节都不是。

表达方式 ++[[]][+[]]+[+[]] 可能最初看起来相当气馁和模糊,但实际上相对容易分解成单独的表达。下面我简单地添加了括号以便清楚;我可以向你保证,他们什么也没有改变,但如果你想验证那么随便读一读 分组操作员。因此,表达式可以更清楚地写成

( ++[[]][+[]] ) + ( [+[]] )

打破这一点,我们可以通过观察来简化 +[] 评估为 0。为了满足自己为什么这是真的,请查看 一元+算子 并沿着稍微曲折的小径走,最后走 ToPrimitive 将空数组转换为空字符串,然后最终转换为空字符串 0 通过 ToNumber。我们现在可以替代 0 对于每个实例 +[]

( ++[[]][0] ) + [0]

已经更简单了。至于 ++[[]][0],这是一个组合 前缀增量运算符 (++),一个 数组文字 使用单个元素定义数组,该数组本身是一个空数组([[]])和a 财产访问者 ([0])调用由数组文字定义的数组。

所以,我们可以简化 [[]][0] 到了 [] 我们有 ++[], 对?事实上,由于评估,情况并非如此 ++[] 抛出错误,这可能最初看起来令人困惑。但是,有点想到了性质 ++ 清楚地表明:它用于增加变量(例如 ++i)或对象属性(例如, ++obj.count)。它不仅评估值,还将值存储在某个地方。如果是 ++[],它无处可放新值(无论它是什么),因为没有引用要更新的对象属性或变量。在规范方面,这由内部涵盖 PutValue 操作,由前缀增量运算符调用。

那么,那是什么呢 ++[[]][0] 做?好吧,通过类似的逻辑 +[],内部数组转换为 0 并且此值增加 1 给我们一个最终价值 1。财产的价值 0 在外部数组中更新为 1 并且整个表达式评估为 1

这让我们失望了

1 + [0]

......这是一个简单的用途 加法运算符。两个操作数都是第一个 转换为原语 如果任一原始值是字符串,则执行字符串连接,否则执行数字加法。 [0] 转换为 "0",因此使用字符串连接,生成 "10"

作为最后的一点,可能不会立即显而易见的是,重写其中任何一个 toString() 要么 valueOf() 的方法 Array.prototype 将更改表达式的结果,因为在将对象转换为原始值时,如果存在,则检查并使用它们。例如,以下内容

Array.prototype.toString = function() {
  return "foo";
};
++[[]][+[]]+[+[]]

......产生 "NaNfoo"。为什么会发生这种情况留给读者练习...


57
2017-12-30 15:41





让我们简单一点:

++[[]][+[]]+[+[]] = "10"

var a = [[]][+[]];
var b = [+[]];

// so a == [] and b == [0]

++a;

// then a == 1 and b is still that array [0]
// when you sum the var a and an array, it will sum b as a string just like that:

1 + "0" = "10"

21
2017-12-28 23:13





这个评估相同但略小

+!![]+''+(+[])
  • [] - 是一个转换后的数组,当你加或减时,转换为0,因此+ [] = 0
  • ![] - 评估为false,因此!! []计算结果为true
  • + !! [] - 将true转换为计算结果为true的数值,因此在本例中为1
  • +'' - 在表达式中附加一个空字符串,使数字转换为字符串
  • + [] - 计算结果为0

所以评估为

+(true) + '' + (0)
1 + '' + 0
"10"

所以现在你知道了,试试这个:

_=$=+[],++_+''+$

13
2017-08-26 08:58



答案是什么? '010'? - ghostCoder
好吧,不,它仍然评估为“10”。然而,这是以不同的方式进行的。尝试在像chrome或其他东西的javascript检查器中对此进行评估。 - Vlad Shlosberg
_ = $ = + [],++ _ +''+ $ - > _ = $ = 0,++ _ +''+ $ - > _ = 0,$ = 0,++ _ +''+ $ - > ++ 0 +''+ 0 - > 1 +''+ 0 - >'10'// Yei:v - LeagueOfJava


+ []的计算结果为0 [...]然后将其与任何事物相加(+操作)将数组内容转换为其字符串表示形式,其中包含用逗号连接的元素。

任何其他类似于获取数组的索引(具有比+操作更重要的优先级)是有序的,并没有什么有趣的。


7
2017-12-30 08:10





也许用最简单的方法将表达式评估为没有数字的“10”是:

+!+[] + [+[]] //“10”

-~[] + [+[]]  //“10”

// ==========说明========== \\

+!+[] : +[] 转换为0。 !0 转换为 true+true 转换为1。 -~[] = -(-1) 这是1

[+[]] : +[] 转换为0。 [0] 是一个单个元素为0的数组。

然后JS评估 1 + [0]因此 Number + Array 表达。然后ECMA规范工作: + 运算符通过调用将两个操作数转换为字符串 toString()/valueOf() 基地的功能 Object 原型。如果表达式的两个操作数仅为数字,则它作为附加函数运行。诀窍是数组很容易将它们的元素转换为连接的字符串表示。

一些例子:

1 + {} //    "1[object Object]"
1 + [] //    "1"
1 + new Date() //    "1Wed Jun 19 2013 12:13:25 GMT+0400 (Caucasus Standard Time)"

有两个很好的例外 Objects 加成结果 NaN

[] + []   //    ""
[1] + [2] //    "12"
{} + {}   //    NaN
{a:1} + {b:2}     //    NaN
[1, {}] + [2, {}] //    "1,[object Object]2,[object Object]"

4