关于javascript:为什么++[[]][+[]]+[+[]] 返回字符串”10″?

Why does ++[[]][+[]]+[+[]] return the string “10”?

这是有效的,并返回javascript中的字符串"10"(这里有更多示例):

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

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


如果我们把它分开,混乱就等于:

1
2
3
++[[]][+[]]
+
[+[]]

在javascript中,+[] === 0是正确的。+将某物转换成数字,在这种情况下,它将归结为+""0(见下面的详细规范)。

因此,我们可以简化它(+++有优势):

1
2
3
++[[]][0]
+
[0]

因为[[]][0]的意思是:从[[]]中得到第一个元素,所以:

  • [[]][0]返回内部数组([])。由于引用的原因,说[[]][0] === []是错误的,但是让我们调用内部数组A以避免错误的符号。
  • ++[[]][0] == A + 1,因为++表示"增量1"。
  • ++[[]][0] === +(A + 1);换句话说,它总是一个数字(+1不一定返回一个数字,而++总是返回一个数字,这要归功于蒂姆指出了这一点)。

同样,我们可以将混乱简化为更清晰的内容。让我们用[]代替A

1
2
3
+([] + 1)
+
[0]

在javascript中,这也是正确的:[] + 1 ==="1",因为[] ==""(加入一个空数组),所以:

  • +([] + 1) === +("" + 1)
  • +("" + 1) === +("1")
  • +("1") === 1

让我们进一步简化它:

1
2
3
1
+
[0]

另外,在javascript:[0] =="0"中也是如此,因为它将一个数组与一个元素结合在一起。连接将连接由,分隔的元素。使用一个元素,您可以推断出这个逻辑将导致第一个元素本身。

因此,最后我们得到(数字+字符串=字符串):

1
2
3
4
5
1
+
"0"

==="10" // Yay!

+[]规格明细:

这是一个相当复杂的迷宫,但对于+[],首先它被转换为字符串,因为这就是+所说的:

11.4.6 Unary + Operator

The unary + operator converts its operand to Number type.

The production UnaryExpression : + UnaryExpression is evaluated as follows:

  • Let expr be the result of evaluating UnaryExpression.

  • Return ToNumber(GetValue(expr)).

  • ToNumber()说:

    Object

    Apply the following steps:

  • Let primValue be ToPrimitive(input argument, hint String).

  • Return ToString(primValue).

  • ToPrimitive()说:

    Object

    Return a default value for the Object. The default value of an object is retrieved by calling the [[DefaultValue]] internal method of the object, passing the optional hint PreferredType. The behaviour of the [[DefaultValue]] internal method is defined by this specification for all native ECMAScript objects in 8.12.8.

    [[DefaultValue]]说:

    8.12.8 [[DefaultValue]] (hint)

    When the [[DefaultValue]] internal method of O is called with hint String, the following steps are taken:

  • Let toString be the result of calling the [[Get]] internal method of object O with argument"toString".

  • If IsCallable(toString) is true then,

  • a. Let str be the result of calling the [[Call]] internal method of toString, with O as the this value and an empty argument list.

    b. If str is a primitive value, return str.

    数组的.toString表示:

    15.4.4.2 Array.prototype.toString ( )

    When the toString method is called, the following steps are taken:

  • Let array be the result of calling ToObject on the this value.

  • Let func be the result of calling the [[Get]] internal method of array with argument"join".

  • If IsCallable(func) is false, then let func be the standard built-in method Object.prototype.toString (15.2.4.2).

  • Return the result of calling the [[Call]] internal method of func providing array as the this value and an empty arguments list.

  • 因此,+[]归结为+"",因为[].join() ===""

    同样,+定义为:

    11.4.6 Unary + Operator

    The unary + operator converts its operand to Number type.

    The production UnaryExpression : + UnaryExpression is evaluated as follows:

  • Let expr be the result of evaluating UnaryExpression.

  • Return ToNumber(GetValue(expr)).

  • ToNumber""定义为:

    The MV of StringNumericLiteral ::: [empty] is 0.

    因此,+"" === 0+[] === 0


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

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

    1
    1+[0].toString() = 10


    以下内容改编自一篇博客文章,回答了我在这个问题还未解决时发表的这个问题。链接是到ecmascript 3规范的(HTML副本),这仍然是当前常用Web浏览器中javascript的基线。

    首先,一个注释:这种表达式永远不会出现在任何(健全的)生产环境中,它只是作为一个练习,在读者了解javascript的脏边方面有多好。JavaScript运算符在类型之间隐式转换的一般原则是有用的,与一些常见的转换一样,但在本例中,大部分细节不是这样的。

    表达式++[[]][+[]]+[+[]]最初可能看起来相当威严和模糊,但实际上相对容易分解为单独的表达式。为了清晰起见,我在下面简单地添加了括号;我可以向您保证,它们什么都不会更改,但是如果您想验证这一点,那么可以随意阅读有关分组运算符的内容。因此,可以更清楚地将表达式写成

    1
    ( ++[[]][+[]] ) + ( [+[]] )

    把这个分解,我们可以通过观察+[]0的评价来简化。为了满足您自己的要求,请检查一元+运算符,并遵循稍微曲折的轨迹,最终将空数组转换为空字符串,然后通过tonumber转换为0。我们现在可以用0代替+[]的每一个实例:

    1
    ( ++[[]][0] ) + [0]

    已经简单了。对于++[[]][0],这是前缀递增运算符(++的组合,数组文字定义一个数组,其中单个元素本身是一个空数组([[]]和一个属性访问器([0]),对数组文字定义的数组调用。

    所以,我们可以把[[]][0]简化为[],我们有++[],对吗?事实上,情况并非如此,因为评估++[]时会抛出一个错误,这可能在最初看起来很混乱。然而,稍微考虑一下++的性质就清楚了:它用于增加变量(例如++i)或对象属性(例如++obj.count)。它不仅计算出一个值,还将该值存储在某个地方。在++[]的情况下,由于没有对要更新的对象属性或变量的引用,因此它没有任何地方可以放置新值(无论它是什么)。在spec术语中,这由内部putValue操作覆盖,该操作由prefix increment运算符调用。

    那么,++[[]][0]做了什么?好吧,根据与+[]相似的逻辑,内部数组转换为0,这个值由1递增,得到1的最终值。外部数组中属性0的值更新为1,整个表达式的计算结果为1

    这让我们

    1
    1 + [0]

    …这是加法运算符的简单用法。两个操作数都首先转换为基元,如果其中一个基元值是字符串,则执行字符串串联,否则执行数字加法。[0]转换为"0",因此使用字符串连接,生成"10"

    最后一点,可能不立即明显的是,重写toString()Array.prototypevalueOf()方法中的任何一种都会改变表达式的结果,因为在将对象转换为原始值时,会检查和使用这两种方法(如果存在)。例如,以下内容

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

    …生产"NaNfoo"。为什么会这样,留给读者作为练习…


    让我们简单点:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    ++[[]][+[]]+[+[]] ="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"

    这一个的计算结果相同,但有点小

    1
    +!![]+''+(+[])
    • []-是一个数组被转换,当您对其进行加或减操作时,该数组被转换为0,因此+[]=0
    • ![]-计算结果为假,因此!![]计算为真
    • +!![]-将true转换为计算结果为true的数值,因此在本例中为1
    • +""-向表达式追加空字符串,导致数字转换为字符串
    • +[]-计算结果为0

    评估结果也是如此

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

    现在你明白了,试试这个:

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


    +[]的计算结果为0[…]然后用任何东西对它求和(+operation),将数组内容转换为由逗号连接的元素组成的字符串表示形式。

    任何其他像获取数组索引(比+操作优先级高)的操作都是有序的,没有什么有趣的。


    可能将表达式计算为"10"而不带数字的最短方法是:

    +!+[] + [+[]]/"10"

    -~[] + [+[]]/"10"

    //说明==\

    +!+[]+[]转换为0。!0转换为true+true转换为1。-~[]=-(-1),即1

    [+[]]+[]转换为0。[0]是一个带有单个元素0的数组。

    然后JS对1 + [0]进行评估,得出Number + Array的表达式。然后ECMA规范开始工作:+操作符通过从基础Object原型调用toString()/valueOf()函数将两个操作数转换为字符串。如果表达式的两个操作数都是数字,则它将作为一个加法函数进行操作。诀窍是数组可以轻松地将其元素转换为连接的字符串表示形式。

    一些例子:

    1
    2
    3
    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
    3
    4
    5
    [] + []   //   ""
    [1] + [2] //   "12"
    {} + {}   //    NaN
    {a:1} + {b:2}     //    NaN
    [1, {}] + [2, {}] //   "1,[object Object]2,[object Object]"

  • 一元加上给定的字符串转换为数字
  • 给定的递增运算符字符串转换并递增1
  • []==''。空字符串
  • +""或+[]的计算结果为0。

    1
    2
    3
    4
    ++[[]][+[]]+[+[]] = 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"

    Why does return the string