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


1657

这是有效的,并返回"10"JavaScript中的字符串(此处有更多示例):

console.log(++[[]][+[]]+[+[]])

为什么?这是怎么回事


446
从理解开始,+[]将一个空数组强制转换为0...,然后浪费一个下午...;)
deceze


10
看一下wtfjs.com-它与explanatinos有很多类似的东西。
ThiefMaster

3
@deceze,您在哪里学这种东西?哪本书?我正在从MDN学习JS,但是他们不教这些东西
Siddharth Thevaril

6
@SiddharthThevaril就像您刚才所做的一样:有人在某处张贴了有关它的信息,而我碰巧读了它。
deceze

Answers:


2070

如果我们将其拆分,则混乱等于:

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

在JavaScript中,确实是这样+[] === 0+将某物转换为数字,在这种情况下,它将归结为+""0(请参见下面的规范详细信息)。

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

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

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

[[]][0]返回内部数组([])。由于引用,说错了[[]][0] === [],但是让我们调用内部数组A以避免错误的表示法。

++在其操作数之前表示“加1并返回加结果”。因此++[[]][0]等于Number(A) + 1(或+A + 1)。

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

(+[] + 1)
+
[0]

+[]将数组强制转换为number之前0,需要先将其强制转换为字符串,即""再次。最后1添加,结果为1

  • (+[] + 1) === (+"" + 1)
  • (+"" + 1) === (0 + 1)
  • (0 + 1) === 1

让我们进一步简化一下:

1
+
[0]

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

在这种情况下,+将看到两个操作数:一个数字和一个数组。现在,它试图将两者强制转换为同一类型。首先,将数组强制转换为字符串"0",然后将数字强制转换为字符串("1")。Number +字符串===String

"1" + "0" === "10" // Yay!

规格详细信息+[]

+[]真是一个迷宫,但要做到这一点,首先要将其转换为字符串,因为这是这样+说的:

11.4.6一元+运算符

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

产生UnaryExpression:+ UnaryExpression的评估如下:

  1. 令expr为评估UnaryExpression的结果。

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

ToNumber() 说:

宾语

应用以下步骤:

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

  2. 返回ToString(primValue)。

ToPrimitive() 说:

宾语

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

[[DefaultValue]] 说:

8.12.8 [[DefaultValue]](提示)

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

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

  2. 如果IsCallable(toString)为true,

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

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

.toString数组的说:

15.4.4.2 Array.prototype.toString()

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

  1. 令array为在this值上调用ToObject的结果。

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

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

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

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

同样,+定义为:

11.4.6一元+运算符

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

产生UnaryExpression:+ UnaryExpression的评估如下:

  1. 令expr为评估UnaryExpression的结果。

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

ToNumber被定义为""

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

如此+"" === 0,如此+[] === 0


8
@harper:这是严格的相等性检查器,即,仅true当值和类型相同时才返回。0 == ""返回true(类型转换之后同),但0 === ""false(未同一类型)。
pimvdb 2011年

41
部分原因是不正确的。该表达式可归结为1 + [0],而不是"1" + [0],因为prefix(++)运算符始终返回一个数字。参见bclary.com/2004/11/07/#a-11.4.4
Tim Down

6
@Tim Down:你是完全正确的。我正在尝试纠正此问题,但是当尝试这样做时,我发现了其他问题。我不确定这怎么可能。++[[]][0]确实会返回1,但++[]会引发错误。这是很了不起的,因为它看起来++[[]][0]确实可以归结为++[]。您也许知道为什么++[]会引发错误,而++[[]][0]不会引发错误吗?
pimvdb 2011年

11
@pimvdb:我很确定问题出PutValue在前缀操作的调用中(在ES3术语8.7.2中)。PutValue需要一个引用,而[]作为一个表达式本身不会产生引用。包含变量引用(例如我们之前定义过var a = []然后++a可以工作)的表达式或对象的属性访问(例如[[]][0])都会生成一个引用。用更简单的术语来说,前缀运算符不仅会产生一个值,而且还需要在某个地方放置该值。
Tim Down

13
@pimvdb:因此,执行var a = []; ++aa为1。执行后++[[]][0][[]]表达式创建的数组现在仅在索引0处包含数字1。++需要引用才能执行此操作。
Tim Down

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

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

1+[0].toString() = 10

7
写起来===比写清楚=>吗?
Mateen Ulhaq '18

61

以下改编自博客文章回答了我在此问题仍处于关闭状态时发布的该问题。链接指向ECMAScript 3规范(的HTML副本),仍然是当今常用的Web浏览器中JavaScript的基线。

首先,发表评论:这种表达永远不会出现在任何(理智的)生产环境中,并且只能用作练习,以了解读者如何了解JavaScript的肮脏边缘。JavaScript运算符在类型之间进行隐式转换的一般原理和某些常见转换一样有用,但本例中的许多细节没有用。

表达式++[[]][+[]]+[+[]]最初看起来可能很强悍和晦涩,但实际上相对容易分解为单独的表达式。为了清楚起见,我在下面仅添加了括号。我可以向您保证,他们什么都不会改变,但是如果您想验证这一点,请随时阅读有关分组运算符的信息。因此,该表达式可以更清楚地写为

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

打破这一下来,我们可以通过观察来简化+[]计算结果为0。为了满足自己的要求,请查看一元+运算符,并遵循稍微曲折的轨迹,最后以ToPrimitive将空数组转换为空字符串,然后将其最终0ToNumber转换为空字符串。现在0,我们可以代替的每个实例+[]

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

已经更简单了。至于++[[]][0],这是前缀递增运算符++),定义一个具有单个元素的数组的数组文字的组合,该元素本身就是一个空数组([[]])和在该数组文字所定义的数组上调用的属性访问器[0])。

因此,我们可以简化 [[]][0]为公正[],我们有++[],对吧?实际上,情况并非如此,因为评估++[]会引发错误,这在开始时可能会引起混淆。但是,对的性质稍加思考即可++明确这一点:它用于递增变量(例如++i)或对象属性(例如++obj.count)。它不仅会求值,还会将该值存储在某个地方。在的情况下++[],它无处可放新值(无论它可能在哪里),因为没有对要更新的对象属性或变量的引用。用规范术语来说,这由内部PutValue操作覆盖,该操作由前缀增量运算符调用。

那么,那是什么 ++[[]][0]办呢?好吧,通过类似的逻辑+[],内部数组被转换为,0并且该值增加,1从而得出最终值10外部数组中的property的值更新为1,整个表达式的求值为1

这给我们留下了

1 + [0]

...这是 加法运算符。首先两个操作数都转换为基元,并且如果任一基元值是字符串,则执行字符串连接,否则执行数字加法。[0]转换为"0",因此使用字符串连接,产生"10"

最后,可能不会立即显而易见的是,覆盖的toString()valueOf()方法之一Array.prototype将更改表达式的结果,因为在将对象转换为原始值时会同时检查和使用二者(如果存在)。例如,以下

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

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


24

让我们简单点:

++[[]][+[]]+[+[]] = "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"

13

该评估结果相同但略小

+!![]+''+(+[])
  • []-是转换后的数组,当您对其进行添加或减去时会转换为0,因此+ [] = 0
  • ![]-评估为false,因此!! []评估为true
  • + !! []-将true转换为计算为true的数值,因此在这种情况下为1
  • +''-将空字符串附加到表达式中,导致数字转换为字符串
  • + []-计算为0

所以估计

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

所以现在您明白了,试试这个:

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

好吧,不,它仍然评估为“ 10”。但是,这样做的方式有所不同。尝试在chrome等类似的JavaScript检查器中对此进行评估。
弗拉德·希洛斯伯格

_ = $ = + [],++ _ +''+ $-> _ = $ = 0,++ _ +''+ $-> _ = 0,$ = 0,++ _ +''+ $ -> ++ 0 +''+ 0-> 1 +''+ 0->'10'// Yei:v
LeagueOfJava

该评估结果相同,但甚至比您的评估结果还小:"10"
ADJenks

7

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

像获取数组索引(具有比+操作更高的优先级)之类的其他东西都是有序的,没什么有趣的。


4

可能的最短方法是将表达式评估为没有数字的“ 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]"

1
  1. 一元加给定的字符串转换为数字
  2. 给定字符串的增量运算符将转换并递增1
  3. [] ==”。空字符串
  4. +''或+ []的值为0。

    ++[[]][+[]]+[+[]] = 10 
    ++[''][0] + [0] : First part is gives zeroth element of the array which is empty string 
    1+0 
    10

1
答案是混乱/混乱,IOW是错误的。[]不是等同于""。首先提取元素,然后通过转换++
PointedEars 2012年

1

逐步执行操作,+将value转换为数字,然后将其添加到一个空数组+[]...因为它为空且等于0,它将

所以从那里开始,现在查看您的代码,它是++[[]][+[]]+[+[]]...

他们之间有加号++[[]][+[]]+[+[]]

所以这些[+[]]将返回,[0]因为它们有一个空数组,该数组将被转换为0另一个数组内部...

就像想象的那样,第一个值是一个二维数组,其中包含一个数组...因此[[]][+[]]等于[[]][0]它将返回[]...

最后++将其转换并增加到1...

因此,您可以想象,1+ "0""10"...

为什么返回字符串“ 10”?

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.