答案 0 :(得分:1983)
如果我们把它分开,这个混乱就等于:
++[[]][+[]]
+
[+[]]
在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的评估如下:
让expr成为评估UnaryExpression的结果。
- 醇>
返回ToNumber(GetValue(expr))。
ToNumber()
说:
对象
应用以下步骤:
让primValue为ToPrimitive(输入参数,提示字符串)。
- 醇>
返回ToString(primValue)。
ToPrimitive()
说:
对象
返回Object的默认值。通过调用对象的[[DefaultValue]]内部方法,传递可选提示PreferredType来检索对象的默认值。 [[DefaultValue]]内部方法的行为由本规范为8.12.8中的所有本机ECMAScript对象定义。
[[DefaultValue]]
说:
8.12.8 [[DefaultValue]](提示)
当使用提示字符串调用O的[[DefaultValue]]内部方法时,将执行以下步骤:
设toString是使用参数“toString”调用对象O的[[Get]]内部方法的结果。
- 醇>
如果IsCallable(toString)为真,那么
一个。令str为调用toString的[[Call]]内部方法的结果,其中O为此值,且为空参数列表。
湾如果str是原始值,则返回str。
数组的.toString
说:
15.4.4.2 Array.prototype.toString()
调用toString方法时,将执行以下步骤:
让数组成为在此值上调用ToObject的结果。
让func成为使用参数“join”调用数组的[[Get]]内部方法的结果。
如果IsCallable(func)为false,则让func成为标准的内置方法Object.prototype.toString(15.2.4.2)。
- 醇>
返回调用func提供数组的[[Call]]内部方法的结果作为此值和空参数列表。
所以+[]
归结为+""
,因为[].join() === ""
。
同样,+
定义为:
11.4.6一元+算子
一元+运算符将其操作数转换为数字类型。
生产UnaryExpression:+ UnaryExpression的评估如下:
让expr成为评估UnaryExpression的结果。
- 醇>
返回ToNumber(GetValue(expr))。
ToNumber
的定义为""
:
StringNumericLiteral ::: [empty]的MV为0。
所以+"" === 0
,因此+[] === 0
。
答案 1 :(得分:117)
++[[]][+[]] => 1 // [+[]] = [0], ++0 = 1
[+[]] => [0]
然后我们有一个字符串连接
1+[0].toString() = 10
答案 2 :(得分:59)
以下内容改编自blog post回答我在此问题仍未结束时发布的问题。链接是ECMAScript 3规范的(HTML副本),仍然是当今常用Web浏览器中JavaScript的基线。
首先,评论:这种表达式永远不会出现在任何(理智的)生产环境中,并且只是作为一种练习用于读者如何知道JavaScript的脏边缘。 JavaScript运算符在类型之间隐式转换的一般原则是有用的,一些常见的转换也是如此,但在这种情况下的大部分细节都不是。
表达式++[[]][+[]]+[+[]]
最初可能看起来相当模糊和模糊,但实际上相对容易分解为单独的表达式。下面我简单地添加括号以便清楚;我可以向你保证他们什么都不会改变,但如果你想验证一下,那么随时可以阅读grouping operator。因此,表达式可以更清楚地写为
( ++[[]][+[]] ) + ( [+[]] )
打破这一点,我们可以通过观察+[]
评估为0
来简化。为了满足自己为什么这样做,请查看unary + operator并按照稍微弯曲的路径,最后以ToPrimitive将空数组转换为空字符串,然后最终转换为{{1} } ToNumber。我们现在可以为0
的每个实例替换0
:
+[]
已经更简单了。至于( ++[[]][0] ) + [0]
,这是prefix increment operator(++[[]][0]
)的组合,array literal定义了一个单元素的数组,该元素本身就是一个空数组++
)和property accessor([[]]
)在数组文字定义的数组上调用。
因此,我们可以将[0]
简化为[[]][0]
,我们有[]
,对吧?事实上,情况并非如此,因为评估++[]
会引发错误,这可能最初看起来令人困惑。但是,对++[]
的性质进行一些考虑就会明确这一点:它用于增加变量(例如++
)或对象属性(例如++i
)。它不仅会评估一个值,还会在某个地方存储该值。在++obj.count
的情况下,它无处放置新值(无论它是什么),因为没有对要更新的对象属性或变量的引用。在规范方面,这由内部PutValue操作覆盖,该操作由前缀增量运算符调用。
那么,++[]
做了什么?好吧,通过与++[[]][0]
类似的逻辑,内部数组转换为+[]
,此值增加0
,使我们得到最终值1
。外部数组中的属性1
的值更新为0
,整个表达式的计算结果为1
。
这给我们留下了
1
...这是addition operator的简单用法。两个操作数都是converted to primitives,如果原始值是字符串,则执行字符串连接,否则执行数字加法。 1 + [0]
转换为[0]
,因此会使用字符串连接,生成"0"
。
最后一点,可能不会立即显而易见的是,覆盖"10"
的{{1}}或toString()
方法中的任何一个都会改变表达式的结果,因为两者都是在将对象转换为原始值时检查并使用(如果存在)。例如,以下
valueOf()
...生成Array.prototype
。为什么会发生这种情况留给读者练习......
答案 3 :(得分:23)
让我们简单一点:
++[[]][+[]]+[+[]] = "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"
答案 4 :(得分:13)
这个评估为相同但略小
+!![]+''+(+[])
所以评估为
+(true) + '' + (0)
1 + '' + 0
"10"
所以现在你知道了,试试这个:
_=$=+[],++_+''+$
答案 5 :(得分:7)
+ []的计算结果为0 [...]然后对任何事物求和(+操作)将数组内容转换为由逗号连接的元素组成的字符串表示。
任何其他类似于获取数组的索引(具有比+操作更重要的优先级)都是有序的,并没有什么有趣的。
答案 6 :(得分: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]"
答案 7 :(得分:1)
+''或+ []评估0。
++[[]][+[]]+[+[]] = 10
++[''][0] + [0] : First part is gives zeroth element of the array which is empty string
1+0
10
答案 8 :(得分:0)
逐步进行操作,+
将值转换为数字,如果将其添加到空数组+[]
...因为它为空且等于0
,它将
所以从现在开始,查看您的代码,它是++[[]][+[]]+[+[]]
...
它们之间有加号++[[]][+[]]
+ [+[]]
因此,这些[+[]]
将返回[0]
,因为它们有一个空数组,该数组将在另一个数组中转换为0
。
可以想象,第一个值是一个内部有一个数组的二维数组...因此[[]][+[]]
等于[[]][0]
,它将返回{{ 1}} ...
最后[]
进行转换并将其增加到++
...
所以您可以想象1
+ 1
将是"0"
...
答案 9 :(得分:0)
++[[]][+[]]+[+[]]
^^^
|
v
++[[]][+[]]+[0]
^^^
|
v
++[[]][0]+[0]
^^^^^^^
|
v
++[]+[0]
^^^
|
v
++[]+"0"
^^^^
|
v
++0+"0"
^^^
|
v
1+"0"
^^^^^
|
v
"10"
+
运算符通过 .valueOf()
强制任何非数字操作数。如果没有返回数字,则调用 .toString()
。
我们可以简单地验证这一点:
const x = [], y = [];
x.valueOf = () => (console.log('x.valueOf() has been called'), y.valueOf());
x.toString = () => (console.log('x.toString() has been called'), y.toString());
console.log(`+x -> ${+x}`);
所以 +[]
与将 ""
强制转换为 0
的数字相同。
如果任何操作数是字符串,则 +
连接起来。