关于javascript:为什么以及如何([![]] + [] [[]])[+!+ [] + [+ []]]评估为字母“i”?

Why and how does ([![]]+[][[]])[+!+[]+[+[]]] evaluate to the letter “i”?

本问题已经有最佳答案,请猛点这里访问。

在阅读Dzone上发表的这篇文章时,我发现了马库斯·拉格伦最初在Twitter上发布的一小段javascript。

下面的代码显然打印了字符串"fail"

1
(![]+[])[+[]]+(![]+[])[+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]];

这涉及到隐式类型转换,我正试图理解这一行的解释方式。

我把每个角色都隔离了

  • (![]+[])[+[]]打印"f"
  • (![]+[])[+!+[]]打印"a"
  • ([![]]+[][[]])[+!+[]+[+[]]]打印"i"
  • (![]+[])[!+[]+!+[]]打印"l"

我还设法将返回每个字母的表达式与"i"分开。

字母"f"

![]空数组是一个对象,根据ecmascript文档,当转换为boolean时,9.2点评估为true,因此这是false

根据第11.6.1点,false+[]二元+运算符的两个参数都转换为字符串,因此我们得到"false"+"",它对"false"进行评估。

+[]如果参数是Object的话,一元加运算符会引起ToNumber转换,随后是ToPrimitive转换。这种转换的结果是通过调用对象的[[DefaultValue]]内部方法来确定的。如果是空数组,则默认为0。(ECMAScript文档,第11.4.6、9.3、9.1节)

"false"[0]我们访问索引0中的字符,因此"f"

字母"a"

同样,这里唯一的区别是方括号部分的附加转换(计算为数字以指向字符串"false"中的另一个字符),由使用一元+!运算符触发。

如上文所述,+[]评估为0

根据第9.2节和第11.4.9节的定义,!0评估为true。首先,0转换为布尔false,然后运算符反转该值。

+true再次,一元加符触发ToNumber转换,它为二进制true返回1。(第11.4.6和9.3节)

"false"[1]返回字符串中的第二个字符,即"a"

字母"L"

!+[]true的评价如上所述。

true+true在原语上使用二进制+触发ToNumber转换。如果为真,则其结果为11+1等于2

"false"[2]—不言自明

字母"i"

让我头疼的是字母"i"。我可以看到第二部分(在方括号中)对字符串"10"进行了计算,第一部分(在括号中)返回"falseundefined",但我不能对这是如何发生的做正面或反面的说明。有人能一步一步地解释吗?尤其是方括号的魔力?(阵列和阵列访问)

如果可能的话,我希望每个步骤都包含到基础ECMAScript规则的链接。

我发现最神秘的部分是:[][[]]


如果你稍微重写一下,你的神秘部分就不那么神秘了:

1
[]['']

由于[]不是整数,因此它将被强制为字符串,因此您要查找的是[]的属性,其名称为''(空字符串)。你只要得到undefined,因为没有这个名字的财产。

至于实际字母,将表达式分成两个主要部分:

  • 字符串([![]]+[][[]])
    • [![]][false]
    • [][[]]undefined
    • 把它们加在一起就得到了"falseundefined"
  • 指数:[+!+[]+[+[]]]。一些空白和括号将使操作更加清晰:[+(!(+[])) + [+[]]]
    • [+[]][0]
    • +[][]强制为整数,所以得到0
    • !+[]0强制为布尔值并将其否定,因此得到true
    • +!+[]强制true为整数,所以得到1
    • 把它们加在一起,就得到了["10"]

当使用字符串访问数组的属性并且该字符串恰好是数组的元素时,该字符串将强制转换为整数,然后返回数组的实际元素:

1
2
3
4
> [1, 2, 3]["0"]
1
> [1, 2, 3]["1"]
2

所以你的最终结果是:

1
2
>"falseundefined"["10"]
"i"

请阅读此答案,了解有关[false] + undefined部分的解释。


([![]]+[][[]])[+!+[]+[+[]]]有两部分:

你发现自己的那个。

![]返回false。然后用[...]得到+.toString()行为。([]+[][].toString()+[].toString()相同)[][[]]是未定义的,因为我们试图访问[]的索引[](或[].toString(),即[]'',这是未定义的。

很抱歉之前的回答,我完全误解了你的评论。