javascript - template - string.raw




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

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

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

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


  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,然后求和(+操作),任何东西都将数组内容转换为由逗号连接的元素组成的字符串表示形式。

任何其他喜欢采取数组的索引(具有比+操作更重要的)是序数,并没有什么意思。


以下内容来自博客文章,回答了我在发布这个问题时仍然关闭的问题。 链接指向ECMAScript 3规范(的HTML副本),它仍然是当今常用Web浏览器中JavaScript的基准。

首先,评论:这种表达方式永远不会出现在任何(理智的)生产环境中,并且只是用于练习读者如何知道JavaScript的肮脏边缘。 JavaScript运算符在类型之间隐式转换的一般原则是有用的,一些常见的转换也是如此,但本例中的大部分细节不是。

表达式++[[]][+[]]+[+[]]可能最初看起来相当具有强制性和模糊性,但实际上相对容易分解为单独的表达式。 下面我简单地加了括号, 我可以向你保证他们没有任何改变,但是如果你想验证一下,可以随时阅读关于分组操作符的信息 。 所以,表达式可以写得更清楚

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

打破这一点,我们可以通过观察+[]求值为0来简化。 为了满足自己为什么这是真的,请检查一元+运算符,并遵循稍微曲折的轨迹,最后ToPrimitive将空数组转换为空字符串,然后通过ToNumber最终将其转换为0 。 我们现在可以用0代替+[]每个实例:

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

更简单了。 至于++[[]][0] ,这是bclary.com/2004/11/07/#a-11.4.4++ )的组合,它定义了一个具有单个元素的数组,它本身是一个空数组( [[]] )和一个属性访问器[0] )调用由数组文字定义的数组。

所以,我们可以将[[]][0]简化为[] ,我们有++[] ,对吧? 事实上,情况并非如此,因为评估++[]会抛出一个错误,这可能最初看起来很混乱。 然而,对++性质的一点想法使得这一点变得清晰:它用于增加一个变量(例如++i )或一个对象属性(例如++obj.count )。 它不仅评估价值,还将价值存储在某个地方。 在++[]的情况下,它没有地方放置新值(不管它可能是什么),因为没有对要更新的对象属性或变量的引用。 在规范中,这由内部PutValue操作覆盖,该操作由前缀增量运算符调用。

那么, ++[[]][0]做什么的? 那么,通过与+[]类似的逻辑,内部数组被转换为0并且该值增加1以给我们最终值1 。 外部数组中的属性0的值更新为1 ,整个表达式的计算结果为1

这给我们留下了

1 + [0]

...这是加法运算符的简单使用。 两个操作数首先ToPrimitive ,如果基元值是一个字符串,则执行字符串连接,否则执行数字加法。 [0]转换为"0" ,所以使用字符串连接,产生"10"

作为一个最终抛开,可能不会立即明白的是,重写Array.prototypetoString()valueOf()方法中的任何一个都会更改表达式的结果,因为如果转换对象转换为原始值。 例如,以下

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

......制作"NaNfoo" 。 为什么会发生这种情况只能作为读者的练习...


如果我们分裂它,混乱等于:

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

在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" ,因为它是用一个元素连接一个数组。 连接将连接由,分隔的元素。 用一个元素,你可以推断出这个逻辑将导致第一个元素本身。

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

1
+
"0"

=== "10" // Yay!

+[]规格细节:

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

11.4.6一元+运算符

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

生产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作为此值和一个空参数列表。

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

数组的.toString表示:

15.4.4.2 Array.prototype.toString()

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

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

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

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

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

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

再次, +被定义为:

11.4.6一元+运算符

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

生产UnaryExpression:+ UnaryExpression的计算方法如下:

  1. 让expr是评估UnaryExpression的结果。

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

ToNumber被定义为""为:

StringNumericLiteral ::: [empty]的MV值为0。

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


这个评估的结果相同但略小一些

+!![]+''+(+[])
  • [] - 是一个转换的数组,当它加上或减去时,转换为0,所以+ [] = 0
  • ![] - 评估为false,因此!! []评估为true
  • + !! [] - 将true转换为数值,其值为true,所以在这种情况下为1
  • +'' - 向表达式添加一个空字符串,使数字转换为字符串
  • + [] - 评估为0

所以评估

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

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

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

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

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

1+[0].toString() = 10




syntax