这是有效的,并在JavaScript中返回字符串"10"
( 此处有更多示例 ):
console.log(++[[]][+[]]+[+[]])
为什么? 这是怎么回事
#1楼
可能的最短方法是将表达式评估为无数字的“ 10”:
+!+[] + [+[]]
//“ 10”
-~[] + [+[]]
//“ 10”
// ==========说明========= \\\\
+!+[]
: +[]
转换为0。 !0
转换为true
。 +true
转换为1。 -~[]
= -(-1)
为1
[+[]]
: +[]
转换为0。 [0]
是具有单个元素0的数组。
然后,JS计算1 + [0]
,即Number + Array
表达式。 然后ECMA规范起作用: +
运算符通过从基本Object
原型调用toString()/valueOf()
函数将两个操作数转换为字符串。 如果一个表达式的两个操作数都只有数字,则它将作为加法函数。 诀窍在于,数组可以轻松地将其元素转换为连接的字符串表示形式。
一些例子:
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]"
#2楼
逐步执行操作,将值+
转换为数字,如果您添加到空数组+[]
...,因为它为空且等于0
,它将
所以从那里开始,现在看一下您的代码,它是++[[]][+[]]+[+[]]
...
它们之间有加号++[[]][+[]]
+ [+[]]
因此,这些[+[]]
将返回[0]
因为它们有一个空数组,该数组在另一个数组中转换为0
...
可以想象,第一个值是一个二维数组,内部有一个数组...因此[[]][+[]]
等于[[]][0]
,它将返回[]
...
最后, ++
将其转换并增加到1
...
因此,您可以想象, 1
+ "0"
将是"10"
...
#3楼
如果我们将其拆分,则混乱等于:
++[[]][+[]]
+
[+[]]
在JavaScript中,确实+[] === 0
。 +
将某物转换为数字,在这种情况下,它将降为+""
或0
(请参见下面的规范详细信息)。
因此,我们可以简化它( ++
优先于+
):
++[[]][0]
+
[0]
因为[[]][0]
意思是:从[[]]
获取第一个元素,所以确实:
[[]][0]
返回内部数组( []
)。 由于引用的原因,说[[]][0] === []
,但让我们调用内部数组A
来避免错误的表示法。
在其操作数之前的++
表示“递增1并返回递增的结果”。 因此++[[]][0]
等于Number(A) + 1
(或+A + 1
)。
同样,我们可以将混乱简化为更清晰的内容。 让我们用[]
代替A
:
(+[] + 1)
+
[0]
在+[]
将数组强制转换为数字0
,需要先将其强制转换为字符串,即再次为""
。 最后,添加1
,得到1
。
-
(+[] + 1) === (+"" + 1)
-
(+"" + 1) === (0 + 1)
-
(0 + 1) === 1
让我们进一步简化一下:
1
+
[0]
同样,在JavaScript中也是如此: [0] == "0"
,因为它是将一个元素与一个数组连接在一起。 加入将通过串联分离的元素,
。 使用一个元素,您可以推断出此逻辑将导致第一个元素本身。
在这种情况下, +
看到两个操作数:一个数字和一个数组。 现在,它试图将两者强制转换为同一类型。 首先,将数组强制转换为字符串"0"
,然后将数字强制转换为字符串( "1"
)。 数字+
字符串===
字符串 。
"1" + "0" === "10" // Yay!
+[]
规范详细信息:
这真是一个迷宫,但要做+[]
,首先要将其转换为字符串,因为那是+
的意思:
11.4.6一元+运算符
一元+运算符将其操作数转换为Number类型。
生产UnaryExpression:+ UnaryExpression的评估如下:
令expr为评估UnaryExpression的结果。
返回ToNumber(GetValue(expr))。
ToNumber()
说:
宾语
应用以下步骤:
令primValue为ToPrimitive(输入参数,提示字符串)。
返回ToString(primValue)。
ToPrimitive()
说:
宾语
返回对象的默认值。 通过调用对象的[[DefaultValue]]内部方法并传递可选提示PreferredType来检索对象的默认值。 本规范为8.12.8中的所有本机ECMAScript对象定义了[[DefaultValue]]内部方法的行为。
[[DefaultValue]]
说:
8.12.8 [[DefaultValue]](提示)
使用提示字符串调用O的[[DefaultValue]]内部方法时,将执行以下步骤:
令toString为使用参数“ toString”调用对象O的[[Get]]内部方法的结果。
如果IsCallable(toString)为true,
一种。 令str为调用toString的[[Call]]内部方法的结果,其中O为this值,并且参数列表为空。
b。 如果str是原始值,则返回str。
数组的.toString
说:
15.4.4.2 Array.prototype.toString()
调用toString方法时,将执行以下步骤:
令array为在this值上调用ToObject的结果。
令func为使用参数“ join”调用array的[[Get]]内部方法的结果。
如果IsCallable(func)为false,则将func设为标准的内置方法Object.prototype.toString(15.2.4.2)。
返回调用func提供数组的[[Call]]内部方法的结果作为this值和空参数列表。
所以+[]
降到+""
,因为[].join() === ""
。
同样, +
定义为:
11.4.6一元+运算符
一元+运算符将其操作数转换为Number类型。
生产UnaryExpression:+ UnaryExpression的评估如下:
令expr为评估UnaryExpression的结果。
返回ToNumber(GetValue(expr))。
ToNumber
为""
定义为:
StringNumericLiteral ::: [空]的MV为0。
因此+"" === 0
,因此+[] === 0
。
#4楼
++[[]][+[]] => 1 // [+[]] = [0], ++0 = 1
[+[]] => [0]
然后我们有一个字符串连接
1+[0].toString() = 10
#5楼
+ []的计算结果为0 [...],然后将其与任何内容求和(+操作),将数组内容转换为其字符串表示形式,该字符串表示形式包含用逗号连接的元素。
像获取数组索引(具有比+操作更高的优先级)之类的其他东西都是有序的,没什么有趣的。
#6楼
以下内容是根据一个博客帖子改编的,该博客帖子回答了我在此问题仍处于关闭状态时发布的该问题。 链接指向ECMAScript 3规范(的HTML副本),仍然是当今常用的Web浏览器中JavaScript的基线。
首先,发表评论:这种表达永远不会出现在任何(理智的)生产环境中,并且只能用作练习,以了解读者如何了解JavaScript的肮脏边缘。 JavaScript运算符在类型之间进行隐式转换的一般原理和一些常见转换一样有用,但是在这种情况下,很多细节都没有。
表达式++[[]][+[]]+[+[]]
最初看起来很气势和晦涩,但实际上相对容易分解为单独的表达式。 为了清楚起见,我在下面仅添加了括号。 我可以向您保证,他们什么都不会改变,但是如果您想验证这一点,请随时阅读有关分组运算符的信息 。 因此,该表达式可以更清楚地写为
( ++[[]][+[]] ) + ( [+[]] )
分解来看,我们可以通过观察+[]
得出0
来简化。 为了使自己满意,请检查一元+运算符,并遵循略带曲折的轨迹,该轨迹最终以ToPrimitive将空数组转换为空字符串,然后最终由ToNumber转换为0
。 现在,我们可以用+[]
每个实例替换0
:
( ++[[]][0] ) + [0]
已经更简单了。 对于++[[]][0]
,它是前缀增量运算符 ( ++
),定义一个带有单个元素的数组的数组文字的组合,该元素本身就是一个空数组( [[]]
)和一个属性访问器 ( [0]
)在由数组常量定义的数组上调用。
因此,我们可以将[[]][0]
简化为[]
而我们有了++[]
,对吗? 实际上,情况并非如此,因为评估++[]
会引发错误,这在开始时可能会引起混淆。 但是,对++
的性质稍加思考就可以清楚地看出这一点:它用于递增变量(例如++i
)或对象属性(例如++obj.count
)。 它不仅会求值,还会将该值存储在某个地方。 在++[]
的情况下,它无处放置新值(无论它可能在哪里),因为没有对要更新的对象属性或变量的引用。 用规范术语来说,这由内部PutValue操作覆盖,该操作由前缀增量运算符调用。
那么, ++[[]][0]
做什么? 好吧,通过与+[]
类似的逻辑,内部数组将转换为0
并且此值增加1
以使我们的最终值为1
。 外部数组中属性0
的值更新为1
,整个表达式的计算结果为1
。
这给我们留下了
1 + [0]
...这是加法运算符的简单用法。 首先将两个操作数都转换为基元,并且如果任一基元值是字符串,则执行字符串连接,否则执行数字加法。 [0]
转换为"0"
,因此使用字符串连接,产生"10"
。
最后一点,可能不是立即显而易见的是,重写Array.prototype
的toString()
或valueOf()
方法之一将更改表达式的结果,因为在转换对象转换为原始值。 例如,以下
Array.prototype.toString = function() {
return "foo";
};
++[[]][+[]]+[+[]]
...产生"NaNfoo"
。 为什么发生这种情况留给读者作为练习...
#7楼
该评估结果相同但略小
+!![]+''+(+[])
- []-是转换后的数组,当您对其进行添加或减去时会转换为0,因此+ [] = 0
- ![]-评估为false,因此!! []评估为true
- + !! []-将true转换为计算为true的数值,因此在这种情况下为1
- +''-将空字符串附加到表达式中,导致数字转换为字符串
- + []-计算为0
所以估计
+(true) + '' + (0)
1 + '' + 0
"10"
所以现在您明白了,试试这个:
_=$=+[],++_+''+$
#8楼
- 一元加给定的字符串转换为数字
- 给定字符串的增量运算符将转换并递增1
- [] ==”。 空字符串
+''或+ []的值为0。
++[[]][+[]]+[+[]] = 10 ++[''][0] + [0] : First part is gives zeroth element of the array which is empty string 1+0 10
#9楼
让我们简单点:
++[[]][+[]]+[+[]] = "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"