"10"
(此处有更多示例): console.log(++[[]][+[]]+[+[]])
为什么?这是怎么回事?
#1 楼
如果将其拆分,则混乱等于:++[[]][+[]]
+
[+[]]
在JavaScript中,确实是
+[] === 0
。 +
将某物转换为数字,在这种情况下,它将归结为+""
或0
(请参见下面的规格详细信息。)因此,我们可以对其进行简化(++
优于+
):++[[]][0]
+
[0]
因为
[[]][0]
的意思是:从[[]]
获得第一个元素,所以确实是:[[]][0]
返回内部数组([]
)。由于引用的原因,[[]][0] === []
是错误的,但让我们调用内部数组A
来避免错误的表示法。所以++
等同于++[[]][0]
(或Number(A) + 1
)。再次,我们可以将混乱简化为更清晰的内容。让我们用
+A + 1
替换为[]
: 。最后,添加了A
,从而得到+[]
。0
""
1
让我们进一步简化它:
/>
(+[] + 1)
+
[0]
另外,在JavaScript中也是这样:
1
,因为它是将一个元素与一个数组连接在一起。连接将串联由(+[] + 1) === (+"" + 1)
分隔的元素。使用一个元素,您可以推断出此逻辑将导致第一个元素本身。在这种情况下,
(+"" + 1) === (0 + 1)
看到两个操作数:一个数字和一个数组。现在,它正试图将两者强制转换为同一类型。首先,将数组强制转换为字符串(0 + 1) === 1
,然后将数字强制转换为字符串([0] == "0"
)。编号,
字符串+
字符串。1
+
[0]
"0"
的规格详细信息:这真是一个迷宫,但是要做
"1"
,首先要将其转换为字符串,因为那是+
所说的:11.4.6一元+运算符
>一元+运算符将其操作数转换为Number类型。
产生式UnaryExpression:+ UnaryExpression的计算方式如下:
让计算结果为UnaryExpression。
返回ToNumber(GetValue(expr))。
===
说:对象
执行以下步骤:
使primValue为ToPrimitive(输入参数,提示字符串)。
返回ToString(primValue)。
+[]
说:对象
返回对象的默认值。通过调用对象的[[DefaultValue]]内部方法并传递可选提示PreferredType来检索对象的默认值。本规范为8.12.8中的所有本机ECMAScript对象定义了[[DefaultValue]]内部方法的行为。
+[]
说:8.12.8 [[DefaultValue]](提示)
用提示字符串调用O的[[DefaultValue]]内部方法时,将执行以下步骤:
让toString为使用参数“ toString”调用对象O的[[Get]]内部方法的结果。
如果IsCallable(toString)为true,则
令str为调用toString的[[Call]]内部方法的结果,其中O为this值,并且参数列表为空。
b。如果str是原始值,则返回str。
数组的
+
说:15.4.4.2 Array.prototype.toString( )
调用toString方法时,将执行以下步骤:
让数组成为在this值上调用ToObject的结果。 >令func为使用参数“ join”调用数组的[[Get]]内部方法的结果。
如果IsCallable(func)为false,则令func为标准内置方法Object.prototype.toString (15.2.4.2)。
返回调用提供数组的func的[[Call]]内部方法的结果作为this值和空参数列表。
因此
ToNumber()
降为ToPrimitive()
,因为[[DefaultValue]]
。再次,
.toString
定义为:11.4.6一元+运算符>
一元+运算符将其操作数转换为Number类型。计算UnaryExpression的结果。
返回ToNumber(GetValue(expr))。
+[]
为+""
定义为:StringNumericLiteral ::: [空]的MV为0。 br />
评论
@harper:这是严格的相等性检查器,即,如果值和类型都相同,则仅返回true。 0 ==“”返回true(在类型转换后相同),但是0 ===“”返回false(非相同类型)。
– pimvdb
2011年8月26日14:04
部分原因是不正确的。表达式归结为1 + [0],而不是“ 1” + [0],因为前缀(++)运算符始终返回数字。参见bclary.com/2004/11/07/#a-11.4.4
– Tim Down
2011年9月9日14:10
@Tim Down:您完全正确。我正在尝试纠正此问题,但是当尝试这样做时,我发现了其他问题。我不确定这怎么可能。 ++ [[]] [0]确实返回1,但是++ []引发错误。这很了不起,因为看起来++ [[]] [0]确实可以归结为++ []。您可能有一个主意,为什么++ []会引发错误,而++ [[]] [0]不会引发错误?
– pimvdb
2011年9月9日14:42
@pimvdb:我很确定问题出在前缀操作的PutValue调用中(用ES3术语,8.7.2)。 PutValue需要引用,而[]作为表达式本身不会产生引用。包含变量引用的表达式(例如,我们之前定义了var a = []然后++ a起作用)或对象的属性访问(例如[[]] [0])都会生成一个Reference。用更简单的术语来说,前缀运算符不仅会产生一个值,而且还需要在某个地方放置该值。
– Tim Down
2011年9月9日15:36
@pimvdb:因此在执行var a = []之后; ++ a的a为1。执行++ [[]] [0]之后,由[[]]表达式创建的数组现在仅包含索引0处的数字1。++需要执行引用才能实现。
– Tim Down
2011年9月9日15:50
#2 楼
++[[]][+[]] => 1 // [+[]] = [0], ++0 = 1
[+[]] => [0]
然后我们有一个字符串连接
1+[0].toString() = 10
评论
写===而不是=>会更清晰吗?
–玛蒂·乌尔哈克(Mateen Ulhaq)
18/12/19在2:46
@MateenUlhaq也许会更清楚一些,但并不完全正确,因为[+ []] === [0]在JS中为假。
–tjespe
11月7日13:06
#3 楼
以下内容是根据一个博客帖子改编的,该博客帖子回答了我在此问题仍处于关闭状态时发布的该问题。链接指向ECMAScript 3规范(的HTML副本),仍然是当今常用的Web浏览器中JavaScript的基线。首先,要发表评论:这种表达永远不会出现在任何(理智的)生产环境,并且仅用作练习,以了解读者如何了解JavaScript的肮脏边缘。 JavaScript运算符在类型之间进行隐式转换的一般原理以及一些常见的转换都是有用的,但在这种情况下,很多细节都没有用。 ,但是将其分解为单独的表达式实际上相对容易。为了清楚起见,我在下面仅添加了括号;我可以向您保证,他们什么都不会改变,但是如果您想验证这一点,请随时阅读有关分组运算符的信息。因此,可以将表达式更清楚地写为
( ++[[]][+[]] ) + ( [+[]] )
简单地说,我们可以通过观察
++[[]][+[]]+[+[]]
取值为+[]
来简化。为了使自己满意,请检查一元+运算符,并遵循略带曲折的轨迹,该轨迹最终以ToPrimitive将空数组转换为空字符串,然后最终由ToNumber转换为0
。现在,我们可以用0
替代0
的每个实例:( ++[[]][0] ) + [0]
已经很简单了。对于
+[]
,它是前缀增量运算符(++[[]][0]
),定义单个元素的数组的数组文字的组合,该元素本身是一个空数组(++
)和一个属性访问器([[]]
),该访问器在由数组文字定义的数组上调用。因此,我们可以将
[0]
简化为[[]][0]
,而我们拥有[]
,对吗?实际上,情况并非如此,因为评估++[]
会引发错误,这在开始时可能会引起混淆。但是,对++[]
的性质稍加思考即可清楚地看出这一点:它用于递增变量(例如++
)或对象属性(例如++i
)。它不仅会求值,还会将该值存储在某个地方。对于++obj.count
,它无处可放新值(无论它可能在哪里),因为没有对要更新的对象属性或变量的引用。用规范术语来说,这由内部PutValue操作覆盖,该操作由前缀增量运算符调用。好吧,通过类似于++[]
的逻辑,内部数组将转换为++[[]][0]
,并且此值增加+[]
,从而得出最终值0
。外部数组中的属性1
的值更新为1
,整个表达式的计算结果为0
。这使我们留下了...这是加法运算符的简单用法。首先将两个操作数都转换为基元,并且如果任一基元值是字符串,则执行字符串连接,否则执行数字加法。
1
转换为1
,因此使用字符串连接,生成[0]
。最后,也许不是立即显而易见的是,覆盖"0"
的"10"
或toString()
方法之一将更改表达式的结果,因为在将对象转换为原始值时会同时检查和使用两者(如果存在)。例如,以下1 + [0]
...产生
valueOf()
。为什么发生这种情况留给读者练习... #4 楼
让我们简单点:++[[]][+[]]+[+[]] = "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"
#5 楼
这个值的计算结果相同,但要小一些。它,因此+ [] = 0
![]-评估为false,因此!! []评估为true
+ !! []-将true转换为数值为是的,因此在这种情况下1
+''-在表达式后附加一个空字符串,导致数字转换为字符串
+ []-值为0
评论
好吧,不,它仍然评估为“ 10”。但是,这样做的方式有所不同。尝试在chrome等类似的JavaScript检查器中对此进行评估。
– Vlad Shlosberg
2011-12-30在6:38
_ = $ = + [],++ _ +''+ $-> _ = $ = 0,++ _ +''+ $-> _ = 0,$ = 0,++ _ +''+ $ -> ++ 0 +''+ 0-> 1 +''+ 0->'10'//是:v
–LeagueOfJava
18年4月28日在4:33
该评估结果相同,但比您的评估结果还小:“ 10”
– ADJenks
1月23日20:44
#6 楼
+ []的值为0[...],然后对其进行加法运算(+操作),将数组内容转换为其字符串表示形式,该字符串表示形式由逗号分隔的元素组成。
数组的索引(比+操作具有更高的优先级)是有序的,没什么有趣的。
#7 楼
可能最短的将表达式赋值为“ 10”而不带数字的方法是: br /> // ==========说明========= \\+!+[] + [+[]]
:-~[] + [+[]]
转换为0。+!+[]
转换为+[]
。 !0
转换为1。true
= +true
等于1 -~[]
:-(-1)
转换为0。[+[]]
是一个具有单个元素0的数组。计算+[]
,即[0]
表达式。然后ECMA规范开始工作:1 + [0]
运算符通过从基本Number + Array
原型调用+
函数将两个操作数转换为字符串。如果一个表达式的两个操作数都为数字,则它将作为加法函数。诀窍在于,数组可以轻松地将其元素转换为串联的字符串表示形式。一些示例:
1 + {} // "1[object Object]"
1 + [] // "1"
1 + new Date() // "1Wed Jun 19 2013 12:13:25 GMT+0400 (Caucasus Standard Time)"
toString()/valueOf()
中的加法结果:[] + [] // ""
[1] + [2] // "12"
{} + {} // NaN
{a:1} + {b:2} // NaN
[1, {}] + [2, {}] // "1,[object Object]2,[object Object]"
#8 楼
一步一步地,+
将值转换为数字,如果将其添加到一个空数组+[]
...因为它是空的并且等于0
,它将所以从那里开始,现在查看您的代码就是
++[[]][+[]]+[+[]]
...就像想象中的那样,第一个值是一个二维数组,里面有一个数组...所以++[[]][+[]]
等于[+[]]
,它将返回[+[]]
... br /> 最后
[0]
将其转换并增加到0
... 所以可以想象,
[[]][+[]]
+ [[]][0]
将是[]
... #9 楼
一元加给定字符串转换为数字
给定字符串转换并以1递增的增量运算符
+''或+ []的值为0。
++[[]][+[]]+[+[]] = 10
++[''][0] + [0] : First part is gives zeroth element of the array which is empty string
1+0
10
评论
答案很混乱/混乱,IOW是错误的。 []不等于“”。首先提取元素,然后用++转换。
– PointedEars
2012年1月12日4:41
评论
首先了解+ []将空数组强制转换为0 ...然后浪费一个下午...;)相关stackoverflow.com/questions/4170978/explain-why-this-works。
看看wtfjs.com-explanatinos有很多类似的功能。
@deceze,您在哪里学这种东西?哪本书?我正在从MDN学习JS,他们不教这些东西
@SiddharthThevaril就像您刚做过的一样:有人在某处张贴了有关它的信息,而我碰巧读了它。