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

时间:2011-08-26 08:46:14

标签: javascript syntax

这是有效的,并在JavaScript(more examples here)中返回字符串"10"

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

为什么呢?这里发生了什么?

10 个答案:

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

     
      
  1. 让expr成为评估UnaryExpression的结果。

  2.   
  3. 返回ToNumber(GetValue(expr))。

  4.   

ToNumber()说:

  

对象

     

应用以下步骤:

     
      
  1. 让primValue为ToPrimitive(输入参数,提示字符串)。

  2.   
  3. 返回ToString(primValue)。

  4.   

ToPrimitive()说:

  

对象

     

返回Object的默认值。通过调用对象的[[DefaultValue]]内部方法,传递可选提示PreferredType来检索对象的默认值。 [[DefaultValue]]内部方法的行为由本规范为8.12.8中的所有本机ECMAScript对象定义。

[[DefaultValue]]说:

  

8.12.8 [[DefaultValue]](提示)

     

当使用提示字符串调用O的[[DefaultValue]]内部方法时,将执行以下步骤:

     
      
  1. 设toString是使用参数“toString”调用对象O的[[Get]]内部方法的结果。

  2.   
  3. 如果IsCallable(toString)为真,那么

  4.         

    一个。令str为调用toString的[[Call]]内部方法的结果,其中O为此值,且为空参数列表。

         

    湾如果str是原始值,则返回str。

数组的.toString说:

  

15.4.4.2 Array.prototype.toString()

     

调用toString方法时,将执行以下步骤:

     
      
  1. 让数组成为在此值上调用ToObject的结果。

  2.   
  3. 让func成为使用参数“join”调用数组的[[Get]]内部方法的结果。

  4.   
  5. 如果IsCallable(func)为false,则让func成为标准的内置方法Object.prototype.toString(15.2.4.2)。

  6.   
  7. 返回调用func提供数组的[[Call]]内部方法的结果作为此值和空参数列表。

  8.   

所以+[]归结为+"",因为[].join() === ""

同样,+定义为:

  

11.4.6一元+算子

     

一元+运算符将其操作数转换为数字类型。

     

生产UnaryExpression:+ UnaryExpression的评估如下:

     
      
  1. 让expr成为评估UnaryExpression的结果。

  2.   
  3. 返回ToNumber(GetValue(expr))。

  4.   

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)

这个评估为相同但略小

+!![]+''+(+[])
  • [] - 是一个转换后的数组,当你加或减时,转换为0,因此+ [] = 0
  • ![] - 评估为false,因此!! []评估为true
  • + !! [] - 将true转换为计算结果为true的数值,因此在本例中为1
  • +'' - 在表达式中附加一个空字符串,使数字转换为字符串
  • + [] - 评估为0

所以评估为

+(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)

  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
    

答案 8 :(得分:0)

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

所以从现在开始,查看您的代码,它是++[[]][+[]]+[+[]] ...

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

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

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

最后[]进行转换并将其增加到++ ...

所以您可以想象1 + 1将是"0" ...

Why does return the string “10”?

答案 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 的数字相同。

如果任何操作数是字符串,则 + 连接起来。