Answers:
如果我们将其拆分,则混乱等于:
++[[]][+[]]
+
[+[]]
在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的评估如下:
令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
。
true
当值和类型相同时才返回。0 == ""
返回true
(类型转换之后同),但0 === ""
是false
(未同一类型)。
++[[]][0]
确实会返回1
,但++[]
会引发错误。这是很了不起的,因为它看起来++[[]][0]
确实可以归结为++[]
。您也许知道为什么++[]
会引发错误,而++[[]][0]
不会引发错误吗?
PutValue
在前缀操作的调用中(在ES3术语8.7.2中)。PutValue
需要一个引用,而[]
作为一个表达式本身不会产生引用。包含变量引用(例如我们之前定义过var a = []
然后++a
可以工作)的表达式或对象的属性访问(例如[[]][0]
)都会生成一个引用。用更简单的术语来说,前缀运算符不仅会产生一个值,而且还需要在某个地方放置该值。
var a = []; ++a
后a
为1。执行后++[[]][0]
,[[]]
表达式创建的数组现在仅在索引0处包含数字1。++
需要引用才能执行此操作。
++[[]][+[]] => 1 // [+[]] = [0], ++0 = 1
[+[]] => [0]
然后我们有一个字符串连接
1+[0].toString() = 10
===
比写清楚=>
吗?
以下改编自博客文章回答了我在此问题仍处于关闭状态时发布的该问题。链接指向ECMAScript 3规范(的HTML副本),仍然是当今常用的Web浏览器中JavaScript的基线。
首先,发表评论:这种表达永远不会出现在任何(理智的)生产环境中,并且只能用作练习,以了解读者如何了解JavaScript的肮脏边缘。JavaScript运算符在类型之间进行隐式转换的一般原理和某些常见转换一样有用,但本例中的许多细节没有用。
表达式++[[]][+[]]+[+[]]
最初看起来可能很强悍和晦涩,但实际上相对容易分解为单独的表达式。为了清楚起见,我在下面仅添加了括号。我可以向您保证,他们什么都不会改变,但是如果您想验证这一点,请随时阅读有关分组运算符的信息。因此,该表达式可以更清楚地写为
( ++[[]][+[]] ) + ( [+[]] )
打破这一下来,我们可以通过观察来简化+[]
计算结果为0
。为了满足自己的要求,请查看一元+运算符,并遵循稍微曲折的轨迹,最后以ToPrimitive将空数组转换为空字符串,然后将其最终0
由ToNumber转换为空字符串。现在0
,我们可以代替的每个实例+[]
:
( ++[[]][0] ) + [0]
已经更简单了。至于++[[]][0]
,这是前缀递增运算符(++
),定义一个具有单个元素的数组的数组文字的组合,该元素本身就是一个空数组([[]]
)和在该数组文字所定义的数组上调用的属性访问器([0]
)。
因此,我们可以简化 [[]][0]
为公正[]
,我们有++[]
,对吧?实际上,情况并非如此,因为评估++[]
会引发错误,这在开始时可能会引起混淆。但是,对的性质稍加思考即可++
明确这一点:它用于递增变量(例如++i
)或对象属性(例如++obj.count
)。它不仅会求值,还会将该值存储在某个地方。在的情况下++[]
,它无处可放新值(无论它可能在哪里),因为没有对要更新的对象属性或变量的引用。用规范术语来说,这由内部PutValue操作覆盖,该操作由前缀增量运算符调用。
那么,那是什么 ++[[]][0]
办呢?好吧,通过类似的逻辑+[]
,内部数组被转换为,0
并且该值增加,1
从而得出最终值1
。0
外部数组中的property的值更新为1
,整个表达式的求值为1
。
这给我们留下了
1 + [0]
...这是 加法运算符。首先将两个操作数都转换为基元,并且如果任一基元值是字符串,则执行字符串连接,否则执行数字加法。[0]
转换为"0"
,因此使用字符串连接,产生"10"
。
最后,可能不会立即显而易见的是,覆盖的toString()
或valueOf()
方法之一Array.prototype
将更改表达式的结果,因为在将对象转换为原始值时会同时检查和使用二者(如果存在)。例如,以下
Array.prototype.toString = function() {
return "foo";
};
++[[]][+[]]+[+[]]
...产生"NaNfoo"
。为什么发生这种情况留给读者作为练习...
该评估结果相同但略小
+!![]+''+(+[])
所以估计
+(true) + '' + (0)
1 + '' + 0
"10"
所以现在您明白了,试试这个:
_=$=+[],++_+''+$
"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]"
+''或+ []的值为0。
++[[]][+[]]+[+[]] = 10
++[''][0] + [0] : First part is gives zeroth element of the array which is empty string
1+0
10
[]
是不是等同于""
。首先提取元素,然后通过转换++
。
+[]
将一个空数组强制转换为0
...,然后浪费一个下午...;)