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

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

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

为什么?这是怎么回事

答案

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

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

在 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 的评估如下:

  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

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

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

1+[0].toString() = 10

以下内容是根据一个博客文章改编而成,该博客文章回答了我在此问题仍处于关闭状态时发布的问题。链接指向 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.prototypetoString()valueOf()方法之一将更改表达式的结果,因为在转换对象转换成原始值。例如,以下

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

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

让我们简单点:

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

该评估结果相同但略小

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

所以估计

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

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

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

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

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

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

逐步执行操作,将值+转换为数字,如果您添加到空数组+[] ...,因为它为空且等于0 ,它将

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

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

因此,这些[+[]]将返回[0]因为它们有一个空数组,该数组在另一个数组中转换为0 ...

可以想象,第一个值是一个二维数组,内部有一个数组... 因此[[]][+[]]等于[[]][0] ,它将返回[] ...

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

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

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